summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorerdgeist <>2003-02-12 17:48:37 +0000
committererdgeist <>2003-02-12 17:48:37 +0000
commitdea6bf757aa9a875eab35b2b650412e7605f1308 (patch)
tree14ed8374c3a3862529313088375693a7de70d3a7
CVS moved to erdgeist.org
-rwxr-xr-xMakefile85
-rwxr-xr-xREADME78
-rwxr-xr-xTODO26
-rwxr-xr-xdebian/changelog16
-rwxr-xr-xdebian/control12
-rwxr-xr-xdebian/copyright12
-rwxr-xr-xdebian/dirs1
-rwxr-xr-xdebian/docs2
-rwxr-xr-xdebian/rules57
-rwxr-xr-xfilters20
-rwxr-xr-xsample-count.fmt73
-rwxr-xr-xsample-erdgeist.fmt74
-rwxr-xr-xsample-mAsq.fmt73
-rwxr-xr-xsample-oldstyle.fmt73
-rwxr-xr-xvchat-client.c536
-rwxr-xr-xvchat-client.sgml376
-rwxr-xr-xvchat-commands.c368
-rwxr-xr-xvchat-config.h127
-rwxr-xr-xvchat-help.h66
-rwxr-xr-xvchat-keygen156
-rwxr-xr-xvchat-messages.h120
-rwxr-xr-xvchat-protocol.c1060
-rwxr-xr-xvchat-ui.c1504
-rwxr-xr-xvchat-user.c374
-rwxr-xr-xvchat.h210
-rwxr-xr-xvchatrc.ex61
26 files changed, 5560 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100755
index 0000000..9f47d28
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,85 @@
1#
2# vchat-client - alpha version
3#
4
5##############################################
6# configuration #
7##############################################
8
9CFLAGS = -Wall -Os #-g -ggdb
10
11## use this line when you've got an readline before 4.(x|2)
12#CFLAGS += -DOLDREADLINE
13
14## you might need one or more of these:
15#CFLAGS += -I/usr/local/ssl/include -L/usr/local/ssl/lib
16#CFLAGS += -I/usr/local/include -L/usr/local/lib
17#CFLAGS += -I/usr/pkg/include -L/usr/pkg/lib
18
19## enable dietlibc
20#CC = diet cc
21#CFLAGS += -static
22
23## enable debug code
24#CFLAGS += -DDEBUG
25
26## the install prefix best is /usr/local
27PREFIX=/usr/local
28
29LIBS = -lncurses -lreadline -lssl -lcrypto
30OBJS = vchat-client.o vchat-ui.o vchat-protocol.o vchat-user.o vchat-commands.o
31
32
33##############################################
34# general targets #
35##############################################
36
37
38all: vchat-client #vchat-client.1
39
40install: vchat-client vchat-keygen vchatrc.ex
41 install -d $(DESTDIR)/etc
42 install -d $(DESTDIR)$(PREFIX)/bin
43 install -d $(DESTDIR)$(PREFIX)/share/man/man1
44 install -m 0755 ./vchat-client $(DESTDIR)$(PREFIX)/bin
45 install -m 0755 ./vchat-keygen $(DESTDIR)$(PREFIX)/bin
46 install -m 0644 ./vchat-client.1 $(DESTDIR)$(PREFIX)/share/man/man1
47 install -m 0644 ./vchatrc.ex $(DESTDIR)/etc/vchatrc
48
49
50uninstall:
51 rm -f $(DESTDIR)$(PREFIX)/bin/vchat-client
52 rm -f $(DESTDIR)$(PREFIX)/bin/vchat-keygen
53 rm -f $(DESTDIR)$(PREFIX)/share/man/man1/vchat-client.1
54 rm -f $(DESTDIR)/etc/vchatrc
55
56
57clean:
58 rm -f .\#* debian/*~ *~ .*~ *.o vchat-client core *.strace \
59 *.ltrace vchat.err vchat.debug* vchat-client.1 manpage.*
60
61
62##############################################
63# compile targets #
64##############################################
65
66vchat-client: $(OBJS)
67 $(CC) $(CFLAGS) -o vchat-client $(OBJS) $(LIBS)
68
69vchat-client.o: vchat-client.c vchat-config.h Makefile
70 $(CC) $(CFLAGS) -o vchat-client.o -c vchat-client.c
71
72vchat-ui.o: vchat-ui.c vchat.h
73 $(CC) $(CFLAGS) -o vchat-ui.o -c vchat-ui.c
74
75vchat-protocol.o: vchat-protocol.c vchat-messages.h vchat.h Makefile
76 $(CC) $(CFLAGS) -o vchat-protocol.o -c vchat-protocol.c
77
78vchat-user.o: vchat-user.c vchat.h
79 $(CC) $(CFLAGS) -o vchat-user.o -c vchat-user.c
80
81vchat-commands.o: vchat-commands.c vchat.h vchat-config.h
82 $(CC) $(CFLAGS) -o vchat-commands.o -c vchat-commands.c
83
84#vchat-client.1: vchat-client.sgml
85# docbook2man vchat-client.sgml
diff --git a/README b/README
new file mode 100755
index 0000000..4e5310f
--- /dev/null
+++ b/README
@@ -0,0 +1,78 @@
1Hi!
2
3this is vchat-client, a GPLed (SSL) client for the vchat protocol.
4
5You can get the most recent version of vchat-client via CVS, e.g:
6
7cvs -d:pserver:anonymous@pulse.flatline.de:/home/cvsroot login
8cvs -z3 -d:pserver:anonymous@pulse.flatline.de:/home/cvsroot co vchat-client
9
10(press return at password prompt)
11
12no autoconf yet, sorry. required libs:
13
14openssl (0.9.6+ preferred)
15readline (4.2+ preferred)
16ncurses (5.2 preferred)
17
18please adjust the Makefile according to your need, then run 'make' to compile,
19./vchat-client to start the binary. nope, I don't have any intention to use
20autoconf for this hack.
21
22run 'fakeroot make -f debian/rules binary' to build a debian package from the
23source.
24
25Keyboard commands:
26
27^J - clear private window
28^O - clear channel window
29^L - redraw screen
30^F - scroll window up
31^B - scroll window down
32^R - change active scrolling window
33 (see rightmost character of topic/console lines)
34^T - shrink private window size
35^G - grow private window size
36^X - toggle private window
37 (pops up on private messages)
38TAB - nick completion
39
40We also do provide a set of IRC like / - commands, there are:
41
42/CONFIG - not implemented yet, but will be used for configuration
43/QUIT reason - issue .x reason
44/USER regex - list users matching regex
45/FLT C regex - add regex to filter list... read ./filters for details
46/RMFLT regex|id - remove filter regex or id from filter list
47/LSFLT (C) - list all filter rules (matching C)
48/CLFLT (C) - remove all filter rules (matching C)
49/M(SG) user msg - issue .m user msg
50/LH - print hilitelist
51/ME action - issue .a action
52
53if you want to use an own window for messages set "messages" to a value in
54~./vchat/config ..
55
56if you want to use an SSL certificate with the default configuration you need
57to put the key in ~/.vchat/key and the certificate in ~/.vchat/cert, both in
58PEM format. You can generate a key and a certificate by calling vchat-keygen,
59but the server you connect needs to know the anon CA provided by vchat-keygen,
60which currently isn't the case anywhere.
61
62FILES:
63
64 README - you're reading it.
65 TODO - list of things to do
66 vchat-client.c - main() and utility functions
67 vchat-config.h - configuration defaults
68 vchat-keygen - key generator with anon CA - server support required
69 vchat-messages.h - server messages <-> function correlation
70 vchat-protocol.c - handling of server connection & messages
71 vchat-ui.c - user-interface and readline handling
72 vchat-user.c - userlist handling
73 vchat.h - global structures and functions
74
75everything should be self-explantory, but please don't refrain to ask and/or
76give bug reports, feature requests, hints and tricks, etc etc ..
77
78 Andreas Kotes <count@flatline.de>
diff --git a/TODO b/TODO
new file mode 100755
index 0000000..ef91851
--- /dev/null
+++ b/TODO
@@ -0,0 +1,26 @@
1- more documentation
2
3- .i with nickchanges
4
5- autologon
6- message-window
7- clock
8- config-file load/safe
9- saveable ignore list
10- known_hosts
11- bell
12- signon / no longer idle notify
13
14- better .u parsing
15
16- non-blocking sends
17
18- url-catcher
19- expire of old messages in scrollbuffer
20- signon/signoff colors
21- don't quit when disconnected externally
22
23- quitmessage (output + config)
24- last line from server sometimes isn't parsed realtime
25- optionally quit when idled out
26
diff --git a/debian/changelog b/debian/changelog
new file mode 100755
index 0000000..17e8bf7
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,16 @@
1vchat-client (0.15-2) unstable; urgency=low
2
3 * changed debian/rules using "make install"
4 * added manpage
5
6 -- Hans Freitag <macrotron@president.eu.org> Wed, 30 Nov 2001 14:47:54 +0200
7
8vchat-client (0.15-1) unstable; urgency=low
9
10 * Current Version
11
12 -- Andreas Kotes <count@flatline.de> Wed, 28 Nov 2001 23:28:54 +0200
13
14Local variables:
15mode: debian-changelog
16End:
diff --git a/debian/control b/debian/control
new file mode 100755
index 0000000..d106fd5
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,12 @@
1Source: vchat-client
2Section: unknown
3Priority: optional
4Maintainer: Andreas Kotes <count@flatline.de>
5Build-Depends: debhelper (>> 3.0.0), libreadline4-dev, libncurses-dev, libssl-dev, docbook-to-man
6Standards-Version: 3.5.2
7
8Package: vchat-client
9Architecture: any
10Depends: ${shlibs:Depends}
11Description: client for the vchat
12 This client uses ssl
diff --git a/debian/copyright b/debian/copyright
new file mode 100755
index 0000000..e066d1d
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,12 @@
1This package was debianized by Sebastian Klemke <cBP@gmx.de> on
2Thu, 23 Aug 2001 18:41:57 +0200.
3
4It was downloaded from berlin.ccc.de
5
6Upstream Author(s): Andreas Kotes <count@flatline.de>
7
8Copyright:
9
10The client is copyright (c) 2001 Andreas Kotes, it is distributed under the
11terms of the GNU General Public License, the GPL should be in
12/usr/share/common-licenses/GPL on a debian system.
diff --git a/debian/dirs b/debian/dirs
new file mode 100755
index 0000000..e772481
--- /dev/null
+++ b/debian/dirs
@@ -0,0 +1 @@
usr/bin
diff --git a/debian/docs b/debian/docs
new file mode 100755
index 0000000..724e084
--- /dev/null
+++ b/debian/docs
@@ -0,0 +1,2 @@
1README
2TODO
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..389c597
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,57 @@
1#!/usr/bin/make -f
2# GNU copyright 1997 to 1999 by Joey Hess.
3
4# Uncomment this to turn on verbose mode.
5#export DH_VERBOSE=1
6
7# This is the debhelper compatability version to use.
8export DH_COMPAT=3
9
10configure: configure-stamp
11configure-stamp:
12 dh_testdir
13 touch configure-stamp
14
15build: configure-stamp build-stamp
16build-stamp:
17 dh_testdir
18 $(MAKE)
19 touch build-stamp
20
21clean:
22 dh_testdir
23 dh_testroot
24 rm -f build-stamp configure-stamp
25 -$(MAKE) clean
26 dh_clean
27
28install: build
29 dh_testdir
30 dh_testroot
31 dh_clean -k
32 dh_installdirs
33 $(MAKE) install DESTDIR=$(CURDIR)/debian/vchat-client PREFIX=/usr
34
35
36# Build architecture-independent files here.
37binary-indep: build install
38# We have nothing to do by default.
39
40# Build architecture-dependent files here.
41binary-arch: build install
42 dh_testdir
43 dh_testroot
44 dh_installdocs
45 dh_installchangelogs
46 dh_link
47 dh_strip
48 dh_compress
49 dh_fixperms
50 dh_installdeb
51 dh_shlibdeps
52 dh_gencontrol
53 dh_md5sums
54 dh_builddeb
55
56binary: binary-indep binary-arch
57.PHONY: build clean binary-indep binary-arch binary install configure
diff --git a/filters b/filters
new file mode 100755
index 0000000..4bb8626
--- /dev/null
+++ b/filters
@@ -0,0 +1,20 @@
1vchat-client now offers a new model of shaping your messages:
2Filters.
3
4Filters can have different colours, where [0-9] stand for
5colour pairs, certain letters of [a-zA-Z] mean character
6attributes. The two "special colours" [+-] are used for
7black/whitelisting your messages, from now on called
8ignore/zoom.
9
10While the alphanumeric colours are used to highlight the
11regex associated with them, the latter case may be used
12to select some of the messages from your scrollback buffer.
13
14This may mean - regex "all but those messages containing
15the regex" or + regex "only those messages containing the
16regex".
17
18When you have ignore regexes, zooming always supersedes
19ignorance, i.e. a message that once is declared as white-
20listed wont be ignored.
diff --git a/sample-count.fmt b/sample-count.fmt
new file mode 100755
index 0000000..019c46e
--- /dev/null
+++ b/sample-count.fmt
@@ -0,0 +1,73 @@
1# !!! This file must be named ~/.vchat/formats in order to work !!!
2
3# please be REALLY REALLY careful here
4# the following lines will be used as
5# format strings
6
7# ^ introduces a style change:
8# TODO: ^^ == '^'
9
10# 0 - default colorpair for window
11# 1 - colorpair 1 RED
12# 2 - colorpair 2 GREEN
13# 3 - colorpair 3 YELLOW
14# 4 - colorpair 4 BLUE
15# 5 - colorpair 5 MAGENTA
16# 6 - colorpair 6 CYAN
17# 7 - colorpair 7 WHITE
18# 8 - colorpair 8 WHITE on RED
19# 9 - colorpair 9 WHITE on BLUE
20# aA - alternate charset on/off
21# bB - bold on/off
22# dD - dim on/off
23# iI - invisible on/off
24# lL - blink on/off
25# nN - normal on/off
26# pP - protected on/off
27# rR - reverse on/off
28# sS - standout on/off
29# uU - underlined on/off
30
31
32# Counts defaults:
33
34FS_PLAIN = "%s"
35FS_CHAN = "%s"
36FS_PRIV = "%s"
37FS_SERV = "^2%s"
38FS_GLOB = "^2%s"
39FS_DBG = "^3%s"
40FS_ERR = "^1%s"
41FS_IDLE = "^8%s"
42FS_TIME = "^5[%H:%M]-- ^0"
43FS_TOPICW = "[ Channel %d: %s"
44FS_NOTOPICW = "[ Channel %d has no topic"
45FS_CONSOLE = "[ %s@%s:%d, use .h to get help, send bugs reports and feature requests to vchat@pulse.flatline.de ..."
46FS_CONNECTED = "^2# Connected to '^6%s^2', port ^6%d^2 ..."
47FS_TOPIC = "^2# Channel ^6%d^2 topic is: '^0%s^2'"
48FS_NOTOPIC = "^2# Channel ^6%d^2 has no topic"
49FS_CHGTOPIC = "^2# ^6%s^2 changes topic to: '^0%s^2'"
50FS_USONLINE = "^2# Users online: %s"
51FS_USMATCH = "^2# Users matching \"%s\":%s"
52FS_SIGNON = "^2# ^6%s^2 %s"
53FS_SIGNOFF = "^2# ^6%s^2 %s"
54FS_JOIN = "^2# ^6%s^2 %s ^6%d"
55FS_LEAVE = "^2# ^6%s^2 %s ^6%d"
56FS_NICKCHANGE = "^2# ^6%s^2 ^6%s^2 %s"
57FS_UNKNOWNMSG = "?? unknown message: %s"
58FS_BOGUSMSG = "?? bogus message: %s"
59FS_RXPUBURL = "^5[^6%s^5]^0 %s"
60FS_MYPUBURL = "^5[^6^u%s^U^5]^0 %s"
61FS_RXPUBMSG = "^5<^6%s^5>^0 %s"
62FS_MYPUBMSG = "^5<^6^u%s^U^5>^0 %s"
63FS_TXPUBMSG = "^5<^6^b%s^B^5>^0 %s"
64FS_RXPRIVMSG = "^5*^6%s^5*^0 %s"
65FS_TXPRIVMSG = "^5-> *^6%s^5*^0 %s"
66FS_BGPRIVMSG = "^1! Bogus message, not sent."
67FS_PUBACTION = "^5*^0 ^6%s^0 %s"
68FS_TXPUBACTION = "^5*^0 ^6^b%s^B^0 %s"
69FS_BGTXPUBACTION = "^1! No action taken."
70FS_COMMAND = "^2## command: %s"
71FS_LOCALCOMMAND = "^2## local command (not executed yet): %s"
72FS_BOGUSCOMMAND = "^2## bogus command (not executed or sent): %s"
73FS_SBINF = " [%d/%d] "
diff --git a/sample-erdgeist.fmt b/sample-erdgeist.fmt
new file mode 100755
index 0000000..cce9ef2
--- /dev/null
+++ b/sample-erdgeist.fmt
@@ -0,0 +1,74 @@
1# !!! This file must be named ~/.vchat/formats in order to work !!!
2
3# please be REALLY REALLY careful here
4# the following lines will be used as
5# format strings
6
7# ^ introduces a style change:
8# TODO: ^^ == '^'
9
10# 0 - default colorpair for window
11# 1 - colorpair 1 RED
12# 2 - colorpair 2 GREEN
13# 3 - colorpair 3 YELLOW
14# 4 - colorpair 4 BLUE
15# 5 - colorpair 5 MAGENTA
16# 6 - colorpair 6 CYAN
17# 7 - colorpair 7 WHITE
18# 8 - colorpair 8 WHITE on RED
19# 9 - colorpair 9 WHITE on BLUE
20# aA - alternate charset on/off
21# bB - bold on/off
22# dD - dim on/off
23# iI - invisible on/off
24# lL - blink on/off
25# nN - normal on/off
26# pP - protected on/off
27# rR - reverse on/off
28# sS - standout on/off
29# uU - underlined on/off
30
31# erdgeists colorset
32
33FS_PLAIN = "%s"
34FS_CHAN = "%s"
35FS_PRIV = "%s"
36FS_SERV = "^1%s"
37FS_GLOB = "^1%s"
38FS_DBG = "^3%s"
39FS_ERR = "^1%s"
40FS_IDLE = "^8%s"
41FS_TIME = "^9[%H:%M]^0 "
42FS_TOPICW = "[ Channel %d: %s"
43FS_NOTOPICW = "[ Channel %d has no topic"
44FS_CONSOLE = "[ %s@%s:%d, use .h to get help"
45FS_CONNECTED = "^2# Connected to '^0%s^2', port ^0%d^2 ..."
46FS_TOPIC = "^1# Channel ^6%d^2 topic is: '^0%s^2'"
47FS_NOTOPIC = "^1# Channel ^6%d^2 has no topic"
48FS_CHGTOPIC = "^1# ^4%s^1 changes topic to: '^0%s^1'"
49FS_USONLINE = "^0# Users online: %s"
50FS_USMATCH = "^0# Users matching \"%s\":%s"
51FS_SIGNON = "^1# ^4%s^1 %s"
52FS_SIGNOFF = "^1# ^4%s^1 %s"
53FS_JOIN = "^1# ^4%s^1 %s ^0%d"
54FS_LEAVE = "^1# ^4%s^1 %s ^0%d"
55FS_NICKCHANGE = "^1# ^4%s^1 ^4%s^1 %s"
56FS_UNKNOWNMSG = "?? unknown message: %s"
57FS_BOGUSMSG = "?? bogus message: %s"
58FS_RXPUBURL = "^0[^4%s^0] %s"
59FS_MYPUBURL = "^0[^4^u%s^U^0] %s"
60FS_RXPUBMSG = "^0<^4%s^0> %s"
61FS_MYPUBMSG = "^0<^4^u%s^U^0> %s"
62FS_TXPUBMSG = "^0<^4^b%s^B^0> %s"
63FS_RXPRIVMSG = "^2>^4%s^2<^0 %s"
64FS_TXPRIVMSG = "^1*^4%s^1*^0 %s"
65FS_BGPRIVMSG = "^1! Bogus message, not sent."
66FS_PUBACTION = "^1*^0 ^4%s^0 %s"
67FS_TXPUBACTION = "^1*^0 ^4^b%s^B^0 %s"
68FS_BGTXPUBACTION = "^1! No action taken."
69FS_COMMAND = "^2## command: %s"
70FS_LOCALCOMMAND = "^2## local command (not executed yet): %s"
71FS_BOGUSCOMMAND = "^2## bogus command (not executed or sent): %s"
72FS_SBINF = " [%d/%d] "
73FS_SBINF_SCROLL = " ^8[%d/%d]^0 "
74
diff --git a/sample-mAsq.fmt b/sample-mAsq.fmt
new file mode 100755
index 0000000..02908b0
--- /dev/null
+++ b/sample-mAsq.fmt
@@ -0,0 +1,73 @@
1# !!! This file must be named ~/.vchat/formats in order to work !!!
2
3# please be REALLY REALLY careful here
4# the following lines will be used as
5# format strings
6
7# ^ introduces a style change:
8# TODO: ^^ == '^'
9
10# 0 - default colorpair for window
11# 1 - colorpair 1 RED
12# 2 - colorpair 2 GREEN
13# 3 - colorpair 3 YELLOW
14# 4 - colorpair 4 BLUE
15# 5 - colorpair 5 MAGENTA
16# 6 - colorpair 6 CYAN
17# 7 - colorpair 7 WHITE
18# 8 - colorpair 8 WHITE on RED
19# 9 - colorpair 9 WHITE on BLUE
20# aA - alternate charset on/off
21# bB - bold on/off
22# dD - dim on/off
23# iI - invisible on/off
24# lL - blink on/off
25# nN - normal on/off
26# pP - protected on/off
27# rR - reverse on/off
28# sS - standout on/off
29# uU - underlined on/off
30
31
32# Counts defaults:
33
34FS_PLAIN = "%s"
35FS_CHAN = "%s"
36FS_PRIV = "%s"
37FS_SERV = "^2%s"
38FS_GLOB = "^2%s"
39FS_DBG = "^3%s"
40FS_ERR = "^1%s"
41FS_IDLE = "^8%s"
42FS_TIME = "^5%H:%M^0 "
43FS_TOPICW = "[ Channel %d: %s"
44FS_NOTOPICW = "[ Channel %d has no topic"
45FS_CONSOLE = "[ %s@%s:%d, use .h to get help, send bugs reports and feature requests to vchat@pulse.flatline.de ..."
46FS_CONNECTED = "^2*^0C^2* Connected to '^6%s^2', port ^6%d^2 ..."
47FS_TOPIC = "^2*^0T^2* Channel ^6%d^2 topic is: '^0%s^2'"
48FS_NOTOPIC = "^2*^0T^2* Channel ^6%d^2 has no topic"
49FS_CHGTOPIC = "^5*^0T^2* ^6%s^2 changes topic to: '^0%s^2'"
50FS_USONLINE = "^2*^0+^2* Users online: %s"
51FS_USMATCH = "^2*^0=^2* Users matching \"%s\":%s"
52FS_SIGNON = "^3*^0>^3* ^6%s^2 %s"
53FS_SIGNOFF = "^1*^0<^1* ^6%s^2 %s"
54FS_JOIN = "^3*^0^b>^B^3* ^6%s^2 %s ^6%d"
55FS_LEAVE = "^1*^0^b<^B^1* ^6%s^2 %s ^6%d"
56FS_NICKCHANGE = "^5*^0N^5* ^6%s^2 ^6%s^2 %s"
57FS_UNKNOWNMSG = "^5*^1!^5*^1 unknown message: %s"
58FS_BOGUSMSG = "^5*^1!^5*^1 bogus message: %s"
59FS_RXPUBURL = "^5[^6%s^5]^0 ^u%s^U"
60FS_MYPUBURL = "^5[^6^u%s^U^5]^0 %s"
61FS_RXPUBMSG = "^5<^6%s^5>^0 %s"
62FS_MYPUBMSG = "^5<^6^u%s^U^5>^0 %s"
63FS_TXPUBMSG = "^5<^6^b%s^B^5>^0 %s"
64FS_RXPRIVMSG = "^5[^6^u%s^U^5]^0 %s"
65FS_TXPRIVMSG = "^5>^6^b%s^B^5<^0 %s"
66FS_BGPRIVMSG = "^5*^1!^5*^1 Bogus message, not sent."
67FS_PUBACTION = "^5*^0 ^6%s^0 %s"
68FS_TXPUBACTION = "^5*^0 ^6^b%s^B^0 %s"
69FS_BGTXPUBACTION = "^1! No action taken."
70FS_COMMAND = "^2*^5I^2* command: %s"
71FS_LOCALCOMMAND = "^5*^1!^5^1 local command (not executed yet): %s"
72FS_BOGUSCOMMAND = "^5*^1!^5^1 bogus command (not executed or sent): %s"
73FS_SBINF = " [%d/%d] "
diff --git a/sample-oldstyle.fmt b/sample-oldstyle.fmt
new file mode 100755
index 0000000..a1b0eb5
--- /dev/null
+++ b/sample-oldstyle.fmt
@@ -0,0 +1,73 @@
1# !!! This file must be named ~/.vchat/formats in order to work !!!
2
3# please be REALLY REALLY careful here
4# the following lines will be used as
5# format strings
6
7# ^ introduces a style change:
8# TODO: ^^ == '^'
9
10# 0 - default colorpair for window
11# 1 - colorpair 1 RED
12# 2 - colorpair 2 GREEN
13# 3 - colorpair 3 YELLOW
14# 4 - colorpair 4 BLUE
15# 5 - colorpair 5 MAGENTA
16# 6 - colorpair 6 CYAN
17# 7 - colorpair 7 WHITE
18# 8 - colorpair 8 WHITE on RED
19# 9 - colorpair 9 WHITE on BLUE
20# aA - alternate charset on/off
21# bB - bold on/off
22# dD - dim on/off
23# iI - invisible on/off
24# lL - blink on/off
25# nN - normal on/off
26# pP - protected on/off
27# rR - reverse on/off
28# sS - standout on/off
29# uU - underlined on/off
30
31
32# Counts defaults:
33
34FS_PLAIN = "^0%s"
35FS_CHAN = "^0%s"
36FS_PRIV = "^0%s"
37FS_SERV = "^0%s"
38FS_GLOB = "^0%s"
39FS_DBG = "^0%s"
40FS_ERR = "^0^b%s^B"
41FS_IDLE = "^0^r%s^R"
42FS_TIME = "^0%H:%M "
43FS_TOPICW = "Channel %d: %s"
44FS_NOTOPICW = "Channel %d has no topic"
45FS_CONSOLE = "-=- VChat V0.1 -=- type .h for help -=-"
46FS_CONNECTED = "^0* Connected to '%s', port %d ..."
47FS_TOPIC = "^0* Channel %d topic is: '%s'"
48FS_NOTOPIC = "^0* Channel %d has no topic"
49FS_CHGTOPIC = "^0* %s changed the channel topic to '%s'"
50FS_USONLINE = "^0* Users online: %s"
51FS_USMATCH = "^0* Users matching \"%s\":%s"
52FS_SIGNON = "^0* %s %s"
53FS_SIGNOFF = "^0* %s %s"
54FS_JOIN = "^0* %s %s %d"
55FS_LEAVE = "^0* %s %s %d"
56FS_NICKCHANGE = "^0* %s changed h** nick to %s"
57FS_UNKNOWNMSG = "^0* unknown message: %s"
58FS_BOGUSMSG = "^0* bogus message: %s"
59FS_RXPUBURL = "^0[%s] %s"
60FS_MYPUBURL = "^0[%s] %s"
61FS_RXPUBMSG = "^0<%s> %s"
62FS_MYPUBMSG = "^0<^u%s^U> %s"
63FS_TXPUBMSG = "^0<%s> ^b%s^B"
64FS_RXPRIVMSG = "^0*%s* %s"
65FS_TXPRIVMSG = "^0^b.m %s %s^B"
66FS_BGPRIVMSG = "^0* Bogus message, not sent."
67FS_PUBACTION = "^0%s %s"
68FS_TXPUBACTION = "^0^b.a %s %s^B"
69FS_BGTXPUBACTION = "^1! No action taken."
70FS_COMMAND = "^0* command: %s"
71FS_LOCALCOMMAND = "^0* local command (not executed yet): %s"
72FS_BOGUSCOMMAND = "^0* bogus command (not executed or sent): %s"
73FS_SBINF = " %d/%d "
diff --git a/vchat-client.c b/vchat-client.c
new file mode 100755
index 0000000..ae369ab
--- /dev/null
+++ b/vchat-client.c
@@ -0,0 +1,536 @@
1/*
2 * vchat-client - alpha version
3 * vchat-client.c - main() and utility functions
4 *
5 * Copyright (C) 2001 Andreas Kotes <count@flatline.de>
6 *
7 * This program is free software. It can be redistributed and/or modified,
8 * provided that this copyright notice is kept intact. This program is
9 * distributed in the hope that it will be useful, but without any warranty;
10 * without even the implied warranty of merchantability or fitness for a
11 * particular purpose. In no event shall the copyright holder be liable for
12 * any direct, indirect, incidental or special damages arising in any way out
13 * of the use of this software.
14 *
15 */
16
17/* general includes */
18#include <sys/types.h>
19#include <sys/time.h>
20#include <string.h>
21#include <unistd.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <errno.h>
25#include <fcntl.h>
26#include <signal.h>
27#include <readline/readline.h>
28#include <openssl/ssl.h>
29#include "vchat.h"
30
31/* version of this module */
32unsigned char *vchat_cl_version = "$Id$";
33
34/* externally used variables */
35/* we're logged in */
36unsigned int loggedin = 0;
37/* we run as long as this is true */
38unsigned int status = 1;
39/* error string to show after exit */
40unsigned char errstr[ERRSTRSIZE] = "\0";
41
42/* locally global variables */
43/* our list of filedescriptors */
44static fd_set masterfds;
45
46/* declaration of configuration array */
47#include "vchat-config.h"
48
49/* servers filedescriptor from vchat-protocol.c */
50extern int serverfd;
51
52void setnoption (unsigned char *, unsigned char *);
53
54static void parsecfg(unsigned char *line) {
55 int bytes;
56 unsigned char *param=line;
57 unsigned char *value=NULL;
58
59 /* handle quotes value is empty, so wecan use it */
60 value = strchr(line,'#');
61 if (value) { /* the line contains a cute little quote */
62 value[0]='\0'; /* ignore the rest of the line */
63 }
64
65 /* now split the line into two parts */
66 value = strchr(line,'=');
67 if (!value) return; /* exit if strchr fails */
68 value[0]='\0';
69 value++;
70
71 /* "trim" values */
72 while ((value[0] == ' ')||(value[0] == '\t'))
73 value++;
74 bytes = strlen(value);
75 while ((value[bytes-1] == ' ')||(value[bytes-1] == '\t')) {
76 value[bytes-1] = '\0';
77 bytes=strlen(value);
78 }
79 /* bytes should be strlen(value) */
80 if ( value[bytes-1] == '"' ) value[bytes-1] = '\0';
81 if ( value[0] == '"' ) value++;
82
83 /* "trim" param */
84 while ((param[0] == ' ')||(param[0] == '\t'))
85 param++;
86 bytes = strlen(param);
87 while ((param[bytes-1] == ' ')||(param[bytes-1] == '\t')) {
88 param[bytes-1] = '\0';
89 bytes=strlen(param);
90 }
91 /* bytes should be strlen(param) */
92 if ( param[bytes-1] == '\"' ) param[bytes-1] = '\0';
93 if ( param[0] == '\"' ) param++;
94
95 if ((!param)||(!value)) return; /* failsave */
96
97 //fprintf(stderr,"\"%s\" -> \"%s\"\n",param,value);
98 setnoption(param,value);
99}
100
101static void parseformats(unsigned char *line) {
102 int i;
103 unsigned char *tmp = NULL;
104
105 /* read a format line from file, syntax is
106 FS_XXX = "formatstring"
107 */
108
109 while( *line == ' ') line++;
110
111 if( *line != '#') /* allow to comment out the line */
112 for (i = 0; formatstrings[i].formatstr; i++)
113 if (!strncasecmp(formatstrings[i].idstring, line, strlen( formatstrings[i].idstring) ))
114 {
115 unsigned char *tail = line + strlen( formatstrings[i].idstring);
116 while( *tail==' ' || *tail=='\t') tail++; /* and skip whitespaces */
117
118 if( *tail++ == '=' )
119 {
120 while( *tail==' ' || *tail=='\t') tail++;
121 if( *(tail++)=='\"' )
122 {
123 int j, k = 0, stringends = 0, backslash=0;
124 for ( j = 0; tail[j] && !stringends; j++)
125 {
126 switch( tail[j] ) {
127 case '^':
128 if ( tail[j+1] != '^' )
129 tmpstr[k++] = 1;
130 break;
131 case '\\':
132 backslash=1-backslash;
133 tmpstr[k++] = '\\';
134 break;
135 case '\"':
136 if (backslash) k--; else stringends = 1;
137 default:
138 tmpstr[k++] = tail[j];
139 backslash = 0;
140 }
141 }
142
143 if ( stringends && ( (tmp = (unsigned char *)malloc( 1 + j )) != NULL ) )
144 {
145 memcpy( tmp, tmpstr, k);
146 tmp[k-1]=0;
147 formatstrings[i].formatstr = tmp;
148 }
149 }
150 }
151 }
152
153}
154
155/* UNUSED uncomment if needed
156static void parseknownhosts(unsigned char *line) {
157}
158*/
159
160/* load config file */
161static void
162loadcfg (unsigned char *file,void (*lineparser) (unsigned char *))
163{
164 int fd;
165 int bytes,bufoff=0;
166 unsigned char *tmp = NULL;
167#define BUFSIZE 4096
168 unsigned char buf[BUFSIZE]; /* data buffer */
169 unsigned char *ltmp = buf;
170 unsigned char *tildex = NULL;
171 buf[BUFSIZE-1] = '\0'; /* sanity stop */
172
173 if (!file) return;
174 if (!file[0]) return;
175 if (file[0] == '~')
176 tildex = tilde_expand(file);
177 else
178 tildex = file;
179 fd = open(tildex,O_RDONLY);
180 if (fd == -1) {
181 snprintf (errstr, TMPSTRSIZE, "Can't open config-file \"%s\": %s.", tildex, sys_errlist[errno]);
182 } else {
183 while ((bytes = read(fd,&buf[bufoff],BUFSIZE-bufoff-1))) {
184 if (bytes < 0) {
185 close(fd);
186 return;
187 } else {
188 /* terminate string */
189 buf[bytes + bufoff] = '\0';
190 /* as long as there are lines .. */
191 while ((tmp = strchr (ltmp, '\n')) != NULL) {
192 /* did the server send CR+LF instead of LF with the last line? */
193 if (tmp[-1] == '\r')
194 tmp[-1] = '\0';
195
196 /* remove newline from previous message, advance pointer of next
197 * message */
198 tmp[0] = '\0';
199 tmp++;
200
201 /* we have a last message? give away to line handler! */
202 if (ltmp[0])
203 {
204 lineparser(ltmp);
205 }
206
207 /* move line along .. */
208 ltmp = tmp;
209 }
210 /* buffer exhausted, move partial line to start of buffer and go
211 * on .. */
212 bufoff = (bytes+bufoff) - (ltmp-buf);
213 if (bufoff > 0)
214 memmove (buf, ltmp, bufoff);
215 else
216 bufoff = 0;
217 }
218 }
219 close(fd);
220 }
221}
222
223void
224loadconfig (unsigned char *file)
225{
226 loadcfg(file,parsecfg);
227}
228
229void
230loadformats (unsigned char *file)
231{
232 loadcfg(file,parseformats);
233}
234
235/* get-format-string */
236unsigned char *
237getformatstr (formtstr id)
238{
239 int i;
240 for (i = 0; formatstrings[i].formatstr; i++)
241 if (formatstrings[i].id == id) return formatstrings[i].formatstr;
242 return NULL;
243}
244
245/* get-string-option, fetches *char-value of variable named by option */
246unsigned char *
247getstroption (confopt option)
248{
249 int i;
250#ifdef DEBUG
251 fprintf(stderr,"getstroption: %d\n",option);
252#endif
253 for (i = 0; configoptions[i].type != CO_NIL; i++)
254 if ((configoptions[i].id == option) && (configoptions[i].type == CO_STR)) {
255 if (!configoptions[i].value)
256 return configoptions[i].defaultvalue;
257 else
258 return configoptions[i].value;
259 }
260 return NULL;
261}
262
263/* set-string-option, puts *char-value to variable named by option */
264void
265setstroption (confopt option, unsigned char *string)
266{
267 int i;
268#ifdef DEBUG
269 fprintf(stderr,"setstroption: %d to %s\n",option,string);
270#endif
271 for (i = 0; configoptions[i].type != CO_NIL; i++)
272 if ((configoptions[i].id == option) && (configoptions[i].type == CO_STR)) {
273 if (configoptions[i].value)
274 free(configoptions[i].value);
275 if (string)
276 configoptions[i].value = strdup(string);
277 else
278 configoptions[i].value = NULL;
279 if (configoptions[i].localvar)
280 *configoptions[i].localvar = configoptions[i].value;
281 }
282}
283
284/* set-named-option, puts string to variable named by name */
285void
286setnoption (unsigned char *name, unsigned char *string)
287{
288 int i;
289#ifdef DEBUG
290 fprintf(stderr,"setstrnoption: %s to %s\n",name,string);
291#endif
292 for (i = 0; configoptions[i].type != CO_NIL; i++)
293 if (!strcmp(configoptions[i].varname,name)) {
294 if (configoptions[i].type == CO_STR) {
295 if (configoptions[i].value)
296 free(configoptions[i].value);
297 if (string)
298 configoptions[i].value = strdup(string);
299 else
300 configoptions[i].value = NULL;
301 } else if (configoptions[i].type == CO_INT) {
302 configoptions[i].value = (char *) atoi(string);
303 }
304 if (configoptions[i].localvar)
305 *configoptions[i].localvar = configoptions[i].value;
306 }
307}
308
309/* get-integer-option, fetches int-value of variable named by option */
310int
311getintoption (confopt option)
312{
313 int i;
314#ifdef DEBUG
315 fprintf(stderr,"getintoption: %d\n",option);
316#endif
317 for (i = 0; configoptions[i].type != CO_NIL; i++)
318 if ((configoptions[i].id == option) && (configoptions[i].type == CO_INT)) {
319 if ((int)configoptions[i].value == -1)
320 return (int) configoptions[i].defaultvalue;
321 else
322 return (int) configoptions[i].value;
323 }
324 return 0;
325}
326
327/* set-integer-option, puts int-value to variable named by option */
328void
329setintoption (confopt option, int value)
330{
331 int i;
332#ifdef DEBUG
333 fprintf(stderr,"setintoption: %d to %d\n",option,value);
334#endif
335 for (i = 0; configoptions[i].type != CO_NIL; i++)
336 if ((configoptions[i].id == option) && (configoptions[i].type == CO_INT)) {
337 configoptions[i].value = (char *) value;
338 if (configoptions[i].localvar)
339 *configoptions[i].localvar = configoptions[i].value;
340 }
341}
342
343int quitrequest = 0;
344
345/* cleanup-hook, for SIGINT */
346void
347cleanup (int signal)
348{
349 if( signal == SIGINT ) {
350 switch( quitrequest >> 2 ) {
351 case 0:
352 flushout( );
353 writeout( " Press Ctrl+C twice now to confirm ");
354 showout( );
355 quitrequest+=4;
356 return;
357 break;
358 case 1:
359 flushout( );
360 writeout( " Press Ctrl+C twice now to confirm ");
361 writeout( " Press Ctrl+C once now to confirm ");
362 showout( );
363 quitrequest+=4;
364 return;
365 break;
366 default:
367 break;
368 }
369 }
370 /* restore terminal state */
371 exitui ();
372 /* clear userlist */
373 ul_clear ();
374 /* close server connection */
375 if (serverfd > 0)
376 close (serverfd);
377 /* inform user if we where killed by signal */
378 if (signal > 1)
379 {
380 fprintf (stderr, "vchat-client: terminated with signal %d.\n", signal);
381 } else if (errstr[0])
382 fprintf (stderr, errstr);
383 /* end of story */
384 exit (0);
385}
386
387void calleverysecond( void ) {
388 /* timetriggered execution, don't rely on being called every 1000us */
389 /* rather see it as a chance for being called 9 times in 10 seconds */
390 /* so check time() */
391
392 if(quitrequest)
393 quitrequest--;
394 if(outputcountdown && !--outputcountdown)
395 hideout( );
396}
397
398/* this function is called in the master loop */
399void
400eventloop (void)
401{
402 /* get fresh copy of filedescriptor list */
403 fd_set readfds = masterfds;
404 struct timeval tv = { 1, 0};
405
406 switch (select (serverfd + 1, &readfds, NULL, NULL, &tv))
407 {
408 case -1:
409 /* EINTR is most likely a SIGWINCH - ignore for now */
410 if (errno != EINTR)
411 {
412 snprintf (tmpstr, TMPSTRSIZE, "Select fails, %s.", sys_errlist[errno]);
413 strncpy(errstr,tmpstr,TMPSTRSIZE-2);
414 errstr[TMPSTRSIZE-2] = '\0';
415 strcat(errstr,"\n");
416 writecf (FS_ERR,tmpstr);
417 /* see this as an error condition and bail out */
418 status = 0;
419 }
420 break;
421 case 0:
422 /* time out reached */
423 calleverysecond();
424 break;
425 default:
426 /* something to read from user & we're logged in or have a cert? */
427 if (FD_ISSET (0, &readfds) && loggedin)
428 userinput ();
429
430 /* something to read from server? */
431 if (FD_ISSET (serverfd, &readfds))
432 networkinput ();
433 break;
434 }
435}
436
437void usage(unsigned char *name) {
438 printf("usage: %s [-C config-file] [-l] [-z] [-s host] [-p port] [-c channel] [-n nickname]\n",name);
439 printf(" -C load a second config-file, overriding the first one\n");
440 printf(" -l local connect (no SSL + connects localhost:2323)\n");
441 printf(" -z don't use certificate files\n");
442 printf(" -s set server (default \"%s\")\n",getstroption(CF_SERVERHOST));
443 printf(" -p set port (default %d)\n",getintoption(CF_SERVERPORT));
444 printf(" -c set channel (default %d)\n",getintoption(CF_CHANNEL));
445 if (nick)
446 printf(" -n set nickname (default \"%s\")\n",nick);
447 else
448 printf(" -n set nickname\n");
449 printf(" -f set from (default \"%s\")\n",getstroption(CF_FROM));
450 printf(" -h gives this help\n");
451}
452
453/* main - d'oh */
454int
455main (int argc, char **argv)
456{
457 int pchar;
458 int cmdsunparsed = 1;
459
460 loadconfig (GLOBAL_CONFIG_FILE);
461 loadconfig (getstroption (CF_CONFIGFILE));
462 loadformats(GLOBAL_FORMAT_FILE);
463 loadformats(getstroption (CF_FORMFILE));
464
465 /* parse commandline */
466 while (cmdsunparsed) {
467 pchar = getopt(argc,argv,"C:lzs:p:c:n:f:h");
468#ifdef DEBUG
469 fprintf(stderr,"parse commandline: %d ('%c'): %s\n",pchar,pchar,optarg);
470#endif
471
472 switch (pchar) {
473 case -1 : cmdsunparsed = 0; break;
474 case 'C': loadconfig(optarg); break;
475 case 'l': setintoption(CF_USESSL,0); break;
476 case 'z': setintoption(CF_USECERT,0); break;
477 case 's': setstroption(CF_SERVERHOST,optarg); break;
478 case 'p': setintoption(CF_SERVERPORT,strtol(optarg,NULL,10)); break;
479 case 'c': setintoption(CF_CHANNEL,strtol(optarg,NULL,10)); break;
480 case 'n': setstroption(CF_NICK,optarg); break;
481 case 'f': setstroption(CF_FROM,optarg); break;
482 case 'h': usage(argv[0]); exit(0); break;
483 default : usage(argv[0]); exit(1);
484 }
485 }
486
487 if (optind < argc) { usage(argv[0]); exit(1); }
488
489 if (!getintoption(CF_USESSL)) {
490 setstroption(CF_SERVERHOST,"localhost");
491 setintoption(CF_SERVERPORT,2323);
492 } else {
493 SSL_library_init ();
494 SSL_load_error_strings ();
495 }
496
497
498 /* install signal handler */
499 signal (SIGINT, cleanup);
500 signal (SIGHUP, cleanup);
501 signal (SIGTERM, cleanup);
502 signal (SIGQUIT, cleanup);
503
504 /* initialize userinterface */
505 initui ();
506
507 /* attempt connection */
508 if (!vcconnect (getstroption(CF_SERVERHOST), getintoption(CF_SERVERPORT)))
509 {
510 snprintf (tmpstr, TMPSTRSIZE, "Could not connect to server, %s.",
511 sys_errlist[errno]);
512 strncpy(errstr,tmpstr,TMPSTRSIZE-2);
513 errstr[TMPSTRSIZE-2] = '\0';
514 strcat(errstr,"\n");
515 writecf (FS_ERR,tmpstr);
516 /* exit condition */
517 status = 0;
518 }
519 else
520 {
521 /* add stdin & server to masterdfs */
522 FD_ZERO (&masterfds);
523 FD_SET (0, &masterfds);
524 FD_SET (serverfd, &masterfds);
525 }
526
527 while (status)
528 eventloop ();
529
530 /* sanely close connection to server */
531 vcdisconnect ();
532
533 /* call cleanup-hook without signal */
534 cleanup (0);
535 return 0;
536}
diff --git a/vchat-client.sgml b/vchat-client.sgml
new file mode 100755
index 0000000..ee0dc36
--- /dev/null
+++ b/vchat-client.sgml
@@ -0,0 +1,376 @@
1<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN" [
2
3<!-- Process this file with docbook2man to generate an nroff manual
4 page: `docbook2man manpage.sgml'. You may view
5 the manual page with: `man ./manpage.1'. A typical entry in a
6 Makefile or Makefile.am is:
7
8manpage.1: manpage.sgml
9docbook2man $<
10 -->
11
12 <!ENTITY dhsection "<manvolnum>1</manvolnum>">
13 <!ENTITY dhemail "<email>macrotron@president.eu.org</email>">
14 <!ENTITY dhusername "Hans Freitag">
15]>
16
17<refentry>
18
19<refentryinfo>
20 <address>
21 &dhemail;
22 </address>
23 <author>
24 <firstname>Hans</firstname>
25 <surname>Freitag</surname>
26 </author>
27 <copyright>
28 <year>2001</year>
29 <holder>Andreas Kotes</holder>
30 </copyright>
31</refentryinfo>
32
33
34<refmeta>
35 <refentrytitle>vchat-client</refentrytitle>
36 &dhsection;
37</refmeta>
38
39
40<refnamediv><refname>vchat-client</refname><refpurpose>this is vchat-client, a GPLed (SSL)
41client for the vchat protocol.</refpurpose></refnamediv>
42
43
44<refsynopsisdiv>
45<cmdsynopsis>
46<command>vchat-client</command>
47<arg>-C <replaceable>configfile</replaceable></arg>
48<arg>-l</arg>
49<arg>-z</arg>
50<arg>-s <replaceable>host</replaceable></arg>
51<arg>-p <replaceable>port</replaceable></arg>
52<arg>-c <replaceable>channel</replaceable></arg>
53<arg>-n <replaceable>nickname</replaceable></arg>
54<arg>-f <replaceable>from</replaceable></arg>
55</cmdsynopsis>
56</refsynopsisdiv>
57
58
59<refsect1>
60<title>DESCRIPTION</title>
61<para>vchat-client connects to vchat chat system via ssl</para>
62</refsect1>
63
64
65<refsect1>
66<title>OPTIONS</title>
67<variablelist>
68
69<varlistentry>
70<term><option>-C</option> <replaceable>configfile</replaceable></term>
71<listitem><para>load an alternative configuration file,
72overiding the parameters in the default config files. -C should
73be the first parameter, options set before this parameter can be
74overridden in the configfile.</para></listitem>
75</varlistentry>
76
77<varlistentry>
78<term><option>-l</option></term>
79<listitem><para>local connect (no SSL + connects localhost:2323)</para></listitem>
80</varlistentry>
81
82<varlistentry>
83<term><option>-z</option></term>
84<listitem><para>don't use certificate files</para></listitem>
85</varlistentry>
86
87<varlistentry>
88<term><option>-s</option> <replaceable>host</replaceable></term>
89<listitem><para>local connect (no SSL + connects localhost:2323)</para></listitem>
90</varlistentry>
91
92<varlistentry>
93<term><option>-p</option> <replaceable>port</replaceable></term>
94<listitem><para>set port (default 2325)</para></listitem>
95</varlistentry>
96
97<varlistentry>
98<term><option>-n</option> <replaceable>nickname</replaceable></term>
99<listitem><para>set nickname</para></listitem>
100</varlistentry>
101
102<varlistentry>
103<term><option>-f</option> <replaceable>from</replaceable></term>
104<listitem><para>set from (default "vc-alpha-0.15")</para></listitem>
105</varlistentry>
106
107<varlistentry>
108<term><option>-h</option></term>
109<listitem><para>prints out a help screen</para></listitem>
110</varlistentry>
111
112</variablelist>
113</refsect1>
114
115
116
117<refsect1>
118<title>CONFIGFILE</title>
119<para><command>/etc/vchatrc</command> and <command>~/.vchat/config</command>
120are the default config files of vchat-client. A third config file can be
121given as command line Parameter.</para>
122
123<literallayout>
124
125Hierachie of config files and Options.
126 1. /etc/vchatrc
127 2. ~/vchat/config
128 3. options set before -C option
129 4. options in the config
130 file set with -C option
131 5. options set after -C option
132
133</literallayout>
134
135<para>The config file syntax is "option=value"! it can be used to
136configure the behavior of vchat-client</para>
137
138<variablelist>
139
140<varlistentry>
141<term><option>nick</option></term>
142<listitem><para>Sets the nickname (default is NULL)</para></listitem>
143</varlistentry>
144
145<varlistentry>
146<term><option>from</option></term>
147<listitem><para>Sets the from (default is "vc-alpha-0.15")</para></listitem>
148</varlistentry>
149
150<varlistentry>
151<term><option>host</option></term>
152<listitem><para>Sets the host to connect to (default is pulse.flatline.de)</para></listitem>
153</varlistentry>
154
155<varlistentry>
156<term><option>port</option></term>
157<listitem><para>Set the port (default 2325)</para></listitem>
158</varlistentry>
159
160<varlistentry>
161<term><option>ciphers</option></term>
162<listitem><para>Set ciphers ("HIGH:MEDIUM")</para></listitem>
163</varlistentry>
164
165<varlistentry>
166<term><option>certfile</option></term>
167<listitem><para>Set the certfile (default ~/.vchat/cert)</para></listitem>
168</varlistentry>
169
170<varlistentry>
171<term><option>keyfile</option></term>
172<listitem><para>Set the keyfile (default ~/.vchat/key)</para></listitem>
173</varlistentry>
174
175<varlistentry>
176<term><option>formatfile</option></term>
177<listitem><para>Set the formatfile (default ~/.vchat/formats) not impemented yet!</para></listitem>
178</varlistentry>
179
180<varlistentry>
181<term><option>usessl</option></term>
182<listitem><para>1|0 default=1</para></listitem>
183</varlistentry>
184
185<varlistentry>
186<term><option>usecert</option></term>
187<listitem><para>1|0 default=1</para></listitem>
188</varlistentry>
189
190<varlistentry>
191<term><option>usetime</option></term>
192<listitem><para>1|0 default=1</para></listitem>
193</varlistentry>
194
195<varlistentry>
196<term><option>messages</option></term>
197<listitem><para>set the heigth of the messages window default
198is 0 (no messages window)</para></listitem>
199</varlistentry>
200
201<varlistentry>
202<term><option>hscroll</option></term>
203<listitem><para>default=5 I don't know what this does, but I will
204know in future. maybe you want to tell me.</para></listitem>
205</varlistentry>
206
207<varlistentry>
208<term><option>channel</option></term>
209<listitem><para>number (default=0)</para></listitem>
210</varlistentry>
211
212<varlistentry>
213<term><option>privscrollb</option></term>
214<listitem><para>default=2048</para></listitem>
215</varlistentry>
216
217<varlistentry>
218<term><option>scrollback</option></term>
219<listitem><para>default=8192</para></listitem>
220</varlistentry>
221
222<varlistentry>
223<term><option>privscrollt</option></term>
224<listitem><para>default=0</para></listitem>
225</varlistentry>
226
227<varlistentry>
228<term><option>scrolltime</option></term>
229<listitem><para>default=86400</para></listitem>
230</varlistentry>
231
232</variablelist>
233
234</refsect1>
235
236
237<refsect1>
238<title>KEYS</title>
239
240<literallayout>
241Ctrl+J clear private window
242Ctrl+O clear channel window
243Ctrl+L redraw screen
244Ctrl+F scroll window up
245Ctrl+B scroll window down
246Ctrl+R change active scrolling window
247 (see rightmost character of
248 topic/console lines)
249Ctrl-T shrink private window size
250Ctrl-G grow private window size
251Ctrl+X toggle private window
252 (pops up on private messages)
253TAB nick completion
254</literallayout>
255
256</refsect1>
257
258
259
260
261
262<refsect1>
263<title>DEVEL</title>
264
265<para>You can get the most recent version of vchat-client via CVS, e.g:
266<screen>
267<prompt>$ </prompt><userinput>cvs -d:pserver:anonymous@pulse.flatline.de:/home/cvsroot login</userinput>
268<prompt>$ </prompt><userinput>cvs -z3 -d:pserver:anonymous@pulse.flatline.de:/home/cvsroot co vchat-client</userinput>
269</screen>
270</para>
271
272<para>(press return at password prompt) no autoconf yet, sorry. required libs:
273<literallayout>
274 - openssl (0.9.6+ preferred)
275 - readline (4.2+ preferred)
276 - ncurses (5.2 preferred)
277</literallayout>
278</para>
279
280<para>please adjust the Makefile according to your need, then run 'make' to compile,
281'make install' to install and ./vchat-client to start the binary.
282nope, I don't have any intention to use autoconf for this hack.</para>
283
284<para>run '<command>fakeroot make -f debian/rules binary</command>' or
285'<command>dpkg-buildpackage -rfakeroot</command>' to build a debian
286package from the source.</para>
287
288<para>The Makefile knows the targets "all, install, uninstall, clean"</para>
289
290<literallayout>
291
292FILES:
293 README - you're reading it.
294 TODO - list of things to do
295 vchat-client.c - main() and utility functions
296 vchat-config.h - configuration defaults
297 vchat-keygen - key generator with anon CA -
298 server support required
299 vchat-messages.h - server messages -- function
300 correlation
301 vchat-protocol.c - handling of server
302 connection & messages
303 vchat-ui.c - user-interface and readline
304 handling
305 vchat-user.c - userlist handling
306 vchat.h - global structures and functions
307 debian/ - the debian package build
308 directory
309</literallayout>
310
311<para>everything should be self-explantory, but please don't refrain to ask and/or
312give bug reports, feature requests, hints and tricks, etc etc ..</para>
313
314<para>Andreas Kotes <email>count@flatline.de</email></para>
315
316</refsect1>
317
318
319
320<refsect1>
321<title>SSL</title>
322
323<para>if you want to use an SSL certificate with the default configuration you need
324to put the key in ~/.vchat/key and the certificate in ~/.vchat/cert, both in
325PEM format. You can generate a key and a certificate by calling vchat-keygen,
326but the server you connect needs to know same anon CA provided by
327vchat-keygen, which currently isn't supported anywhere.</para>
328
329<para>to upload your certificate use nick-request.pl from the vchat-server
330source available at ftp://vchat.vaxbusters.org. Thisfor at least one of
331the admins has to be logged in.</para>
332
333</refsect1>
334
335
336
337<refsect1>
338
339<title>SEE ALSO</title>
340<para>gcc (1), cvs (1).</para>
341
342</refsect1>
343
344
345
346<refsect1>
347<title>AUTHOR</title>
348<para>This manual page was written by Hans Freitag &dhemail; for
349vchat-client system. Permission is granted to copy, distribute
350and/or modify this document under the terms of the
351<acronym>GNU</acronym> Free Documentation License, Version 1.1
352or any later version published by the Free Software Foundation;
353with no Invariant Sections, no Front-Cover
354Texts and no Back-Cover Texts.</para>
355</refsect1>
356
357
358
359</refentry>
360
361<!-- Keep this comment at the end of the file
362Local variables:
363mode: sgml
364sgml-omittag:t
365sgml-shorttag:t
366sgml-minimize-attributes:nil
367sgml-always-quote-attributes:t
368sgml-indent-step:2
369sgml-indent-data:t
370sgml-parent-document:nil
371sgml-default-dtd-file:nil
372sgml-exposed-tags:nil
373sgml-local-catalogs:nil
374sgml-local-ecat-files:nil
375End:
376-->
diff --git a/vchat-commands.c b/vchat-commands.c
new file mode 100755
index 0000000..c81bf36
--- /dev/null
+++ b/vchat-commands.c
@@ -0,0 +1,368 @@
1/*
2 * vchat-client - alpha version
3 * vchat-commands.c - handling of client commands
4 *
5 * Copyright (C) 2001 Andreas Kotes <count@flatline.de>
6 *
7 * This program is free software. It can be redistributed and/or modified,
8 * provided that this copyright notice is kept intact. This program is
9 * distributed in the hope that it will be useful, but without any warranty;
10 * without even the implied warranty of merchantability or fitness for a
11 * particular purpose. In no event shall the copyright holder be liable for
12 * any direct, indirect, incidental or special damages arising in any way out
13 * of the use of this software.
14 *
15 */
16
17/* general includes */
18#include <unistd.h>
19#include <errno.h>
20#include <stdio.h>
21#include <string.h>
22
23/* local includes */
24#include "vchat.h"
25#include "vchat-help.h"
26
27/* version of this module */
28unsigned char *vchat_cm_version = "$Id$";
29
30/* our "/command " table */
31enum {
32COMMAND_VERSION,
33COMMAND_FILTERS,
34COMMAND_LSFLT,
35COMMAND_RMFLT,
36COMMAND_CLFLT,
37COMMAND_HELP,
38COMMAND_KEYS,
39COMMAND_QUIT,
40COMMAND_USER,
41COMMAND_FLT,
42COMMAND_PM,
43COMMAND_ACTION,
44COMMAND_PMSHORT,
45COMMAND_PLAIN,
46COMMAND_NONE
47};
48
49static void command_quit ( unsigned char *tail);
50static void command_user ( unsigned char *tail);
51static void command_pm ( unsigned char *tail);
52static void command_action ( unsigned char *tail);
53static void command_help ( unsigned char *tail);
54static void command_flt ( unsigned char *tail);
55static void command_lsflt ( unsigned char *tail);
56static void command_clflt ( unsigned char *tail);
57static void command_rmflt ( unsigned char *tail);
58 void command_version ( unsigned char *tail);
59static void command_none ( unsigned char *line);
60
61static void output_default ( unsigned char *tail);
62
63/* commandentry defined in vchat.h */
64
65static commandentry
66commandtable[] = {
67{ COMMAND_VERSION, "VERSION", 7, command_version, SHORT_HELPTEXT_VERSION, LONG_HELPTEXT_VERSION },
68{ COMMAND_LSFLT, "LSFLT", 5, command_lsflt, NULL, LONG_HELPTEXT_LSFLT },
69{ COMMAND_RMFLT, "RMFLT", 5, command_rmflt, NULL, LONG_HELPTEXT_RMFLT },
70{ COMMAND_CLFLT, "CLFLT", 5, command_clflt, NULL, LONG_HELPTEXT_CLFLT },
71{ COMMAND_HELP, "HELP", 4, command_help, SHORT_HELPTEXT_HELP, LONG_HELPTEXT_HELP },
72{ COMMAND_FILTERS, "FILTERS", 7, command_help, SHORT_HELPTEXT_FILTERS, LONG_HELPTEXT_FILTERS },
73{ COMMAND_KEYS, "KEYS", 4, command_help, SHORT_HELPTEXT_KEYS, LONG_HELPTEXT_KEYS },
74{ COMMAND_QUIT, "QUIT", 4, command_quit, SHORT_HELPTEXT_QUIT, LONG_HELPTEXT_QUIT },
75{ COMMAND_USER, "USER", 4, command_user, SHORT_HELPTEXT_USER, LONG_HELPTEXT_USER },
76{ COMMAND_FLT, "FLT", 3, command_flt, NULL, LONG_HELPTEXT_FLT },
77{ COMMAND_PM, "MSG", 3, command_pm, SHORT_HELPTEXT_MSG, LONG_HELPTEXT_MSG },
78{ COMMAND_ACTION, "ME", 2, command_action, SHORT_HELPTEXT_ME, LONG_HELPTEXT_ME },
79{ COMMAND_PMSHORT, "M", 1, command_pm, NULL, SHORT_HELPTEXT_MSG },
80{ COMMAND_PLAIN, "/", 1, output_default, NULL, NULL },
81{ COMMAND_NONE, "", 0, command_none, NULL, NULL }
82};
83
84/* parse "/command" */
85static int
86translatecommand( unsigned char **cmd)
87{
88 int result;
89 int cut = 0;
90 int maxcut = 0;
91
92 while( (*cmd)[maxcut] && ((*cmd)[maxcut] != 0x20) && ((*cmd)[maxcut] != '\n')) maxcut++;
93 if( maxcut ) maxcut--;
94
95 do {
96 for( result = 0;
97 (result != COMMAND_NONE) &&
98 (strncasecmp(*cmd, commandtable[result].name, commandtable[result].len -
99 ((commandtable[result].len - maxcut - cut > 0) ? cut : 0)));
100 result++);
101 } while ((cut < commandtable[0].len) && (commandtable[result].number == COMMAND_NONE) && (++cut));
102
103 (*cmd) += commandtable[result].len;
104 if( commandtable[result].number != COMMAND_NONE )
105 (*cmd) -= cut;
106
107 return result;
108}
109
110/* handle action */
111static void
112doaction( unsigned char *tail )
113{
114 while( *tail == ' ' ) tail++;
115
116 if( *tail ) {
117 /* send users message to server */
118 snprintf (tmpstr, TMPSTRSIZE, ".a %s", tail);
119 networkoutput (tmpstr);
120
121 /* show action in channel window */
122 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBACTION), nick, tail);
123 writechan (tmpstr);
124 } else {
125
126 /* missing action */
127 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_BGTXPUBACTION));
128 writechan (tmpstr);
129 }
130}
131
132/* handle private message outgoing */
133static void
134privatemessagetx ( unsigned char *tail ) {
135 unsigned char *mesg;
136
137 /* find nick */
138 while( *tail==' ') tail++;
139
140 /* find message */
141 mesg = tail;
142 while ( *mesg && *mesg!=' ') mesg++;
143
144 /* check for nick && message */
145 if(*tail && *mesg) {
146
147 /* terminate nick, move to rel start */
148 *mesg++ = '\0';
149
150 /* form message and send to server */
151 snprintf (tmpstr, TMPSTRSIZE, ".m %s %s", tail, mesg);
152 networkoutput (tmpstr);
153
154 /* show message in private window */
155 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPRIVMSG), tail, mesg);
156 writepriv (tmpstr);
157
158 /* note we messaged someone */
159 ul_msgto(tail);
160
161 } else {
162
163 /* missing nick or message body inform user */
164 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_BGPRIVMSG));
165 writepriv (tmpstr);
166 }
167}
168
169/* handle line entered by user */
170void
171handleline (unsigned char *line)
172{
173#ifdef DEBUG
174 /* debugging? log users input! */
175 fprintf (stderr, "=| %s\n", line);
176#endif
177
178 switch ( line[0] )
179 {
180 case '.':
181 switch ( line[1] ) {
182 case 'm': /* sending a private message? */
183 privatemessagetx( line+2 );
184 break;
185 case 'a':
186 doaction( line+2 );
187 break;
188 case '.':
189 /* .. on start of line is public */
190 if( line[2] != 'm' ) {
191 output_default( line );
192 } else {
193 /* oopsi, "..m " detected */
194 /* dunno what to do */
195 flushout( );
196 writeout("? You probably misstyped ?");
197 writeout(" ");
198 writeout(line );
199 showout( );
200 }
201 break;
202 default:
203
204 /* generic server command, send to server, show to user */
205 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_COMMAND), line);
206 networkoutput (line);
207 writechan (tmpstr);
208 break;
209 }
210 break;
211 case '/':
212 line++;
213 commandtable[translatecommand(&line)].handler(line);
214 break;
215 case '0':
216 break;
217 default:
218 output_default( line );
219 break;
220 }
221}
222
223static void
224output_default( unsigned char *line ) {
225 /* prepare for output on display */
226 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TXPUBMSG), nick, line);
227
228 /* send original line to server */
229 networkoutput (line);
230
231 /* output message to channel window */
232 writechan (tmpstr);
233}
234
235/* handle a "/user " request */
236static void
237command_user( unsigned char *tail)
238{
239 while( *tail == ' ') tail++;
240 if( strlen(tail) >= 3) {
241 unsigned char * out = ul_matchuser( tail);
242 if( *out ) {
243 snprintf( tmpstr, TMPSTRSIZE, getformatstr(FS_USMATCH), tail, out);
244 } else {
245 snprintf( tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), "No user matched that substring");
246 }
247 } else {
248 snprintf( tmpstr, TMPSTRSIZE, getformatstr(FS_ERR), "Specify at least 3 characters to match users");
249 }
250 writechan( tmpstr );
251}
252
253/* handle a "/msg " request */
254static void
255command_pm (unsigned char *tail)
256{
257 privatemessagetx( tail );
258}
259
260/* handle a help request */
261static void
262command_help (unsigned char *line) {
263 flushout( );
264 while( *line==' ') line++;
265 if( *line ) { /* Get help on command */
266 int i;
267 if( ( i = translatecommand( &line ) ) != COMMAND_NONE ) {
268 snprintf( tmpstr, TMPSTRSIZE, "Help on command: %s", commandtable[i].name);
269 writeout( tmpstr );
270 writeout(" ");
271 if( commandtable[i].short_help && !commandtable[i].help )
272 writeout(commandtable[i].short_help );
273 line = commandtable[i].help;
274 if( line ) {
275 while( *line ) {
276 unsigned char *tmp = tmpstr;
277 while( *line && (*line != '\n') )
278 *tmp++ = *line++;
279 *tmp = '\0'; if( *line == '\n') line++;
280 writeout ( tmpstr );
281 }
282 }
283 } else {
284 command_help( " " );
285 }
286 } else { /* Get overall help */
287 int i;
288 for( i = 0; commandtable[i].number != COMMAND_NONE; i++ ) {
289 if( commandtable[i].short_help )
290 writeout( commandtable[i].short_help );
291 }
292 }
293 showout();
294}
295
296/* handle an unknown command */
297static void
298command_none( unsigned char *line) {
299 flushout( );
300 snprintf(tmpstr, TMPSTRSIZE, " Unknown client command: %s ", line);
301 writeout(tmpstr);
302 showout( );
303}
304
305/* handle a "/flt " request */
306static void
307command_flt( unsigned char *tail){
308 unsigned char colour;
309 while(*tail==' ') tail++;
310 colour = *tail++;
311 while( colour && *tail == ' ') tail++;
312 if( colour && *tail) {
313 addfilter( colour, tail);
314 }
315}
316
317/* handle a "/clflt " request */
318static void
319command_clflt ( unsigned char *tail) {
320 while( *tail == ' ') tail++;
321 clearfilters( *tail );
322}
323
324/* handle a "/rmflt " request */
325static void
326command_rmflt ( unsigned char *tail) {
327 while( *tail == ' ') tail++;
328 removefilter( tail );
329}
330
331/* list filters */
332static void
333command_lsflt ( unsigned char *tail) {
334 listfilters();
335}
336
337/* handle a "/me " action */
338static void
339command_action( unsigned char *tail)
340{
341 doaction( tail);
342}
343
344/* handle a "/quit " exit */
345static void
346command_quit ( unsigned char *tail)
347{
348 /* send users message to server */
349 snprintf (tmpstr, TMPSTRSIZE, ".x %s", tail);
350 networkoutput (tmpstr);
351
352 /* show action in channel window */
353 writechan (tmpstr);
354}
355
356/* print out version */
357void
358command_version( unsigned char *tail)
359{
360 /* output internal versions of all modules */
361 flushout();
362 writeout (vchat_cl_version);
363 writeout (vchat_ui_version);
364 writeout (vchat_io_version);
365 writeout (vchat_us_version);
366 writeout (vchat_cm_version);
367 showout();
368}
diff --git a/vchat-config.h b/vchat-config.h
new file mode 100755
index 0000000..d7e7305
--- /dev/null
+++ b/vchat-config.h
@@ -0,0 +1,127 @@
1/*
2 * vchat-client - alpha version
3 * vchat-config.h - declaration of configuration array and default values
4 *
5 * Copyright (C) 2001 Andreas Kotes <count@flatline.de>
6 *
7 * This program is free software. It can be redistributed and/or modified,
8 * provided that this copyright notice is kept intact. This program is
9 * distributed in the hope that it will be useful, but without any warranty;
10 * without even the implied warranty of merchantability or fitness for a
11 * particular purpose. In no event shall the copyright holder be liable for
12 * any direct, indirect, incidental or special damages arising in any way out
13 * of the use of this software.
14 *
15 */
16
17#ifndef GLOBAL_CONFIG_FILE
18# define GLOBAL_CONFIG_FILE "/etc/vchatrc"
19#endif
20
21#ifndef GLOBAL_FORMAT_FILE
22# define GLOBAL_FORMAT_FILE "/etc/vchatformats"
23#endif
24
25/* configuration array with structure as defined in vchat.h */
26extern int usessl;
27extern int usetime;
28extern unsigned int hscroll;
29
30static volatile configoption configoptions[] = {
31/* config-option type name in file default value value localvar */
32 {CF_NICK, CO_STR, "nick", NULL, NULL, &nick },
33 {CF_FROM, CO_STR, "from", "vc-alpha-0.16", NULL, NULL },
34 {CF_SERVERHOST, CO_STR, "host", "pulse.flatline.de",NULL, NULL },
35 {CF_SERVERPORT, CO_INT, "port", (char *) 2325, (char *)-1, NULL },
36 {CF_CIPHERSUITE, CO_STR, "ciphers", "HIGH:MEDIUM", NULL, NULL },
37 {CF_CONFIGFILE, CO_STR, "conffile", "~/.vchat/config", NULL, NULL },
38 {CF_CERTFILE, CO_STR, "certfile", "~/.vchat/cert", NULL, NULL },
39 {CF_KEYFILE, CO_STR, "keyfile", "~/.vchat/key", NULL, NULL },
40 {CF_FORMFILE, CO_STR, "formatfile", "~/.vchat/formats", NULL, NULL },
41 {CF_USESSL, CO_INT, "usessl", (char *) 1, (char *)-1, (unsigned char **)&usessl },
42 {CF_USECERT, CO_INT, "usecert", (char *) 1, (char *)-1, NULL },
43 {CF_USETIME, CO_INT, "usetime", (char *) 1, (char *)-1, (unsigned char **)&usetime },
44 {CF_PRIVHEIGHT, CO_INT, "messages", (char *) 0, (char *)-1, NULL },
45 {CF_HSCROLL, CO_INT, "hscroll", (char *) 5, (char *)-1, (unsigned char **)&hscroll },
46 {CF_CHANNEL, CO_INT, "channel", (char *) 0, (char *)-1, NULL },
47 {CF_SCROLLBPRIV, CO_INT, "privscrollb",(char *) 2048, (char *)-1, NULL },
48 {CF_SCROLLBACK, CO_INT, "scrollback", (char *) 8192, (char *)-1, NULL },
49 {CF_SCROLLBPRIVT,CO_INT, "privscrollt",(char *) 0, (char *)-1, NULL },
50 {CF_SCROLLBACKT, CO_INT, "scrolltime", (char *) 86400, (char *)-1, NULL },
51 {CF_NIL, CO_NIL, NULL, NULL, NULL, NULL },
52};
53
54/*
55choose option with \001 +
560 - default colorpair for window
571 - colorpair 1 RED
582 - colorpair 2 GREEN
593 - colorpair 3 YELLOW
604 - colorpair 4 BLUE
615 - colorpair 5 MAGENTA
626 - colorpair 6 CYAN
637 - colorpair 7 WHITE
648 - colorpair 8 WHITE on RED
659 - colorpair 9 WHITE on BLUE
66aA - alternate charset on/off
67bB - bold on/off
68dD - dim on/off
69iI - invisible on/off
70lL - blink on/off
71nN - normal on/off
72pP - protected on/off
73rR - reverse on/off
74sS - standout on/off
75uU - underlined on/off
76*/
77
78#define FE( ID, STRING) { ID, #ID, STRING }
79
80static formatstring formatstrings[] = {
81/* format-string string */
82 FE( FS_PLAIN, "%s"),
83 FE( FS_CHAN, "%s"),
84 FE( FS_PRIV, "%s"),
85 FE( FS_SERV, "\0012%s"),
86 FE( FS_GLOB, "\0012%s"),
87 FE( FS_DBG, "\0013%s"),
88 FE( FS_ERR, "\0011%s"),
89 FE( FS_ERR_STRING, "\0011%s %s"),
90 FE( FS_IDLE, "\0018%s"),
91 FE( FS_TIME, "\0015[%H:%M]\0010 "),
92 FE( FS_TOPICW, "[ Channel %d: %s"),
93 FE( FS_NOTOPICW, "[ Channel %d has no topic"),
94 FE( FS_CONSOLE, "[ %s@%s:%d, use .h to get help, send bugs reports and feature requests to vchat@pulse.flatline.de ..."),
95 FE( FS_CONNECTED, "\0012# Connected to '\0016%s\0012', port \0016%d\0012 ..."),
96 FE( FS_TOPIC, "\0012# Channel \0016%d\0012 topic is: '\0010%s\0012'"),
97 FE( FS_NOTOPIC, "\0012# Channel \0016%d\0012 has no topic"),
98 FE( FS_CHGTOPIC, "\0012# \0016%s\0012 changes topic to: '\0010%s\0012'"),
99 FE( FS_USMATCH, "\0012# Users matching \"%s\":%s"),
100 FE( FS_USONLINE, "\0012# Users online: %s"),
101 FE( FS_SIGNON, "\0012# \0016%s\0012 %s"),
102 FE( FS_SIGNOFF, "\0012# \0016%s\0012 %s"),
103 FE( FS_JOIN, "\0012# \0016%s\0012 %s \0016%d"),
104 FE( FS_LEAVE, "\0012# \0016%s\0012 %s \0016%d"),
105 FE( FS_NICKCHANGE, "\0012# \0016%s\0012 \0016%s\0012 %s"),
106 FE( FS_UNKNOWNMSG, "?? unknown message: %s"),
107 FE( FS_BOGUSMSG, "?? bogus message: %s"),
108 FE( FS_RXPUBURL, "\0015[\0016%s\0015]\0010 %s"),
109 FE( FS_MYPUBURL, "\0015[\0016\001u%s\001U\0015]\0010 %s"),
110 FE( FS_RXPUBMSG, "\0015<\0016%s\0015>\0010 %s"),
111 FE( FS_MYPUBMSG, "\0015<\0016\001u%s\001U\0015>\0010 %s"),
112 FE( FS_TXPUBMSG, "\0015<\0016\001b%s\001B\0015>\0010 %s"),
113 FE( FS_RXPRIVMSG, "\0015*\0016%s\0015*\0010 %s"),
114 FE( FS_TXPRIVMSG, "\0015-> *\0016%s\0015*\0010 %s"),
115 FE( FS_BGPRIVMSG, "\0011! Bogus message, not sent."),
116 FE( FS_PUBACTION, "\0015*\0010 \0016%s\0010 %s"),
117 FE( FS_TXPUBACTION, "\0015*\0010 \0016\001b%s\001B\0010 %s"),
118 FE( FS_BGTXPUBACTION,"\0011! No action taken."),
119 FE( FS_COMMAND, "\0012## command: %s"),
120 FE( FS_LOCALCOMMAND, "\0012## local command (not executed yet): %s"),
121 FE( FS_BOGUSCOMMAND, "\0012## bogus command (not executed or sent): %s"),
122 FE( FS_SBINF, " [%d/%d] "),
123 FE( FS_MISSTYPED, "\0011* Probably misstyped? Not sent: %s"),
124 FE( FS_UNKNCMD, "\0011* Unknown client command: %s"),
125 FE( FS_BADREGEX, "\0011* Could not compile regex: %s"),
126 FE( FS_PLAIN, NULL)
127};
diff --git a/vchat-help.h b/vchat-help.h
new file mode 100755
index 0000000..9cfe04b
--- /dev/null
+++ b/vchat-help.h
@@ -0,0 +1,66 @@
1/*
2 * vchat-client - alpha version
3 * vchat-help.h - definitions for help on comands
4 *
5 * Copyright (C) 2001 Andreas Kotes <count@flatline.de>
6 *
7 * This program is free software. It can be redistributed and/or modified,
8 * provided that this copyright notice is kept intact. This program is
9 * distributed in the hope that it will be useful, but without any warranty;
10 * without even the implied warranty of merchantability or fitness for a
11 * particular purpose. In no event shall the copyright holder be liable for
12 * any direct, indirect, incidental or special damages arising in any way out
13 * of the use of this software.
14 *
15 */
16
17/* Help strings */
18
19#define SHORT_HELPTEXT_VERSION "/VERSION Prints out version number of all modules"
20#define LONG_HELPTEXT_VERSION NULL
21#define SHORT_HELPTEXT_CONFIG "/CONFIG This feature is not implemented, yet"
22#define LONG_HELPTEXT_CONFIG NULL
23#define SHORT_HELPTEXT_HELP "/HELP [COMMAND] Display this help, or help on COMMAND"
24#define LONG_HELPTEXT_HELP NULL
25#define SHORT_HELPTEXT_RMFLT "/RMFLT [REGEX | ID] Remove regex or id from filter list"
26#define LONG_HELPTEXT_RMFLT SHORT_HELPTEXT_RMFLT
27#define SHORT_HELPTEXT_CLFLT "/CLFLT (C) Remove all filter rules (matching C)"
28#define LONG_HELPTEXT_CLFLT SHORT_HELPTEXT_CLFLT
29#define SHORT_HELPTEXT_LSFLT "/LSFLT (C) List all filter rules (matching C)"
30#define LONG_HELPTEXT_LSFLT SHORT_HELPTEXT_LSFLT
31#define SHORT_HELPTEXT_FLT "/FLT C REGEX Add regex to filter list"
32#define LONG_HELPTEXT_FLT SHORT_HELPTEXT_FLT "\n C may be + - zoom, - - ignore,\n\
33 0 - default, 1 - red, 2 - green,\n\
34 3 - yello, 4 - blue, 5 - magenta,\n\
35 6 - magenta, 7 - white, 8 - white/red\n\
36 9 - blue/red, aA - alt, bB - bold\n\
37 dD - dim, iL - invis, lL - blink\n\
38 nN - normal, pP - protect, rR - reverse,\n\
39 sS - standout, uU - underline"
40#define SHORT_HELPTEXT_KEYS "/HELP KEYS Show summary of key commands"
41#define LONG_HELPTEXT_KEYS " - - List of Commands:\n\
42 ^J - clear private window\n\
43 ^O - clear channel window\n\
44 ^L - redraw screen/scroll to bottom\n\
45 ^F - scroll window up\n\
46 ^B - scroll window down\n\
47 ^R - change active window\n\
48 (watch the *)\n\
49 ^T - shrink private window size\n\
50 ^G - grow private window size\n\
51 ^X - switch off message window or\n\
52 toggle private window\n\
53 TAB - nick completion"
54#define SHORT_HELPTEXT_QUIT "/QUIT [REASON] Leave the chat for reason Reason"
55#define LONG_HELPTEXT_QUIT NULL
56#define SHORT_HELPTEXT_USER "/USER REGEX Lists all users matching regex REGEX"
57#define LONG_HELPTEXT_USER NULL
58#define SHORT_HELPTEXT_MSG "/M[SG] USER MESSAGE Send private message to user USER"
59#define LONG_HELPTEXT_MSG NULL
60#define SHORT_HELPTEXT_ME "/ME ACTION Let the user do an action"
61#define LONG_HELPTEXT_ME NULL
62#define SHORT_HELPTEXT_M SHORT_HELPTEXT_MSG
63#define LONG_HELPTEXT_M LONG_HELPTEXT_MSG
64#define SHORT_HELPTEXT_FILTERS "/HELP FILTERS Show summary of filter commands"
65#define LONG_HELPTEXT_FILTERS SHORT_HELPTEXT_FLT "\n" SHORT_HELPTEXT_RMFLT "\n" SHORT_HELPTEXT_LSFLT "\n" SHORT_HELPTEXT_CLFLT
66
diff --git a/vchat-keygen b/vchat-keygen
new file mode 100755
index 0000000..08c3b6e
--- /dev/null
+++ b/vchat-keygen
@@ -0,0 +1,156 @@
1#!/bin/sh
2
3#
4# vchat-client - alpha version
5# vchat-keygen - generate keypair for SSL with anon CA
6#
7# Copyright (C) 2001 Andreas Kotes <count@flatline.de>
8#
9# This program is free software. It can be redistributed and/or modified,
10# provided that this copyright notice is kept intact. This program is
11# distributed in the hope that it will be useful, but without any warranty;
12# without even the implied warranty of merchantability or fitness for a
13# particular purpose. In no event shall the copyright holder be liable for
14# any direct, indirect, incidental or special damages arising in any way out
15# of the use of this software.
16#
17
18# where do we want to store the key?
19KEYBASE=$1
20if [ "x$KEYBASE" = "x" ] ; then
21 KEYBASE=$HOME/.vchat
22fi
23
24# no key? generate one ...
25if [ ! -e $KEYBASE.key ]; then
26 echo "vchat-keygen: generating RSA key $KEYBASE.key"
27 echo "vchat-keygen: please set passphrase for local security"
28 umask 0077
29 openssl genrsa -des3 -out $KEYBASE.key 2048
30else
31 echo "vchat-keygen: private key $KEYBASE.key exists"
32fi
33
34# no certificate? dump anonymous CA to disk.
35if [ ! -e $KEYBASE.cert ]; then
36 if [ ! -e $KEYBASE.ca.key ]; then
37 echo "vchat-keygen: saving CA key to $KEYBASE.ca.key"
38 cat >$KEYBASE.ca.key <<EOT
39-----BEGIN RSA PRIVATE KEY-----
40MIIEowIBAAKCAQEA0OydKPwRccotlfz4ZhKrVM1vbRa9bWOfZ1c4C6J+iLmZMjuk
41uALo5+c72phZjJ8qXTY+j+J4foRXgBD3qNwDFjtHaDtq4pK70WAWR7+gtnToVjvI
42ngq++Ht82GQQK34QKBBh4U/sdS1qvQWNkW6/tDa61pRwHI9xRhXG3YmVrE0mps/o
43JoaSxQQpk+6nTjuqfbFH+JEqzYgXLLYm5sZ3eRuClCezmQ9a1HSGW+JM4iL7xdRL
44u8o1Ml7PahODIr/Cb4nKco8grJ+gl4ChI+V8VsUNcmmoXdtb7b0x7tCOcvp2TPIE
45VK0sMTDCltGvXDKk3PrL+msAATJhA90FVTUjOQIDAQABAoIBAQCPZoks029J0kLd
4620fID/Jnf6aGkwAsMB/+d4AxhDQjtnivYP7biqvAWRfdH4r/mVQjrJLegczA0ieY
478Ix86552yPNnWLkxkRO3T6ObVa2C7tV2MwytZaTUuzXi9TOgFqQSS8RTOV+MwkKs
48QT59Xy3YDWTK2hHlmJNgTpwz/Tatjv1zTXzDkb+rGLVjpanPoOvSbth2pXJL7kRf
49pkoDWqw59rDC6QQJxucYbueTSlw3YKg6ZJJ9dSsWf8pljCgwW0lLBHVmcsJw3C+z
50mzZW7I4I5wACGaAMjLR1p8bPWC4VF6cP9MdRJ77VZl2//HXb0nE6SHG5V2eDxiRJ
51NMqe/Z8BAoGBAOy6d7wRKADPPwZaLAKaKqsJSiK5DDnXloPgW4/IZrMmokQ0hF7m
52QXXtOvDkewGvIskLXk93/f47RQdGWZ/WRVPDBlxx2VbpxACJLb+EC9BEGOS/emdZ
53DI2934qMhGo4QCSq8I4RTDe0z+55Kj2yVzKv718J1lWaCpC+AtbIB1thAoGBAOHu
54sfcMYV5pV28y77yO/aVRaR19CjnH9mk5kdXLzITy5hYZskgQxmlB/zvMG/nEhAKz
55jwymL7PM0SXM/dWuz54nCYUDHdOexe2DHaFvNaalkziq3eU9B/ANY1+f/nk2TrBm
56+TVaAYWld2X8jcXJbevy3H9IgDfzD27M6tFW1W7ZAoGAT/2eMeVWMBfXgwz7LBHt
578wdbjqoasHzhtkQcjFQ6J7UZRZS9WdfSLMfxj66Uxffo+CgoQRAZuktKwu+Jn1Hm
588SvIPXqW5yBsg4XW+Izk9QXdp4XwFXXooQiUvZtHryC8w+cjC85ag8RMMpesp1ZY
590p7Scrm/PAOmKEycZvkGS2ECgYAWYIjZ2i0Op8pUJixedZ8jr5OEqyzHGkoKk/wg
60u8Wu6Uvmpnbk8lxkcnfwGUAwFcmpZtVlQFR7L28LmmkNr/m6RU2JEgzzN8eMxa66
61nYQn1EBnnWzK1qehnAHap8MRiFJ04E4QfbCm5wOTY1c7Xr73Xp9+L9UbNYSyybL8
62Nuh+yQKBgHUJf3RslTr382pFcHxXNQpA5wQHhtuL+VacbddnZNZCflQoJ+Zk1/GV
630fDgfrY1+LVQvo/rpm6N3FIdLSaFwn2OmZMIwLWfu4BL1NNdWMwjSWkQ8hToVe5e
64707+ARBWPZX0GfZXHUybrZDJNlT01brqo4DhlWxMCPrAj3XNY6yr
65-----END RSA PRIVATE KEY-----
66EOT
67 fi
68 if [ ! -e $KEYBASE.ca.crt ]; then
69 echo "vchat-keygen: saving CA cert to $KEYBASE.ca.crt"
70 cat >$KEYBASE.ca.crt <<EOT
71-----BEGIN CERTIFICATE-----
72MIIC4zCCAcugAwIBAgIBADANBgkqhkiG9w0BAQQFADAbMRkwFwYDVQQDExBBbm9u
73eW1vdXMgRk9PIENBMB4XDTAxMDcwOTE0MzAyM1oXDTExMDcwNzE0MzAyM1owGzEZ
74MBcGA1UEAxMQQW5vbnltb3VzIEZPTyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
75ADCCAQoCggEBANDsnSj8EXHKLZX8+GYSq1TNb20WvW1jn2dXOAuifoi5mTI7pLgC
766OfnO9qYWYyfKl02Po/ieH6EV4AQ96jcAxY7R2g7auKSu9FgFke/oLZ06FY7yJ4K
77vvh7fNhkECt+ECgQYeFP7HUtar0FjZFuv7Q2utaUcByPcUYVxt2JlaxNJqbP6CaG
78ksUEKZPup047qn2xR/iRKs2IFyy2JubGd3kbgpQns5kPWtR0hlviTOIi+8XUS7vK
79NTJez2oTgyK/wm+JynKPIKyfoJeAoSPlfFbFDXJpqF3bW+29Me7QjnL6dkzyBFSt
80LDEwwpbRr1wypNz6y/prAAEyYQPdBVU1IzkCAwEAAaMyMDAwDwYDVR0TAQH/BAUw
81AwEB/zAdBgNVHQ4EFgQUFsM7fh5NPHIgbUBsGqp+IAH4AjIwDQYJKoZIhvcNAQEE
82BQADggEBALKjPE9OX+FrKOODs+d4P/QJdEwsTKwT3zHjxUTKmhIRE1qphAiEfH2g
83IMgr/7y4MZd7FIx84qrfA+a96Yyb5QdbRu0fGlkom1JZxkKOQ2T5SiX7iU2nXMLa
84tsFoqKwrjG4vWwN8ZrlLT72+fZGTtFCUQm7pTxd7UZcfIcmfE43OJGl155gd2X8j
85jbbyu/lBwdJXznK86cm++lvXYJTeJEybipX/XoGoJtCZq0dGyC7vBTGnBZGmNymQ
861QHQ8LjnzGK3q1ccLuGZ9QjXOjMImfPXGxiXMHO63Ph27U3jP4LEBsW3iRaUqevY
87Id4rGHl2/jBQyE1CGeN1o9iZBGmFS1c=
88-----END CERTIFICATE-----
89EOT
90 fi
91 if [ -e /tmp/00.pem ]; then
92 echo "vchat-keygen: insecure files lying around, bailing out"
93 exit
94 fi
95 if [ ! -e $KEYBASE.ca.conf ]; then
96 echo "vchat-keygen: generating config-file for CA $KEYBASE.ca.conf"
97 cat >$KEYBASE.ca.conf <<EOT
98[ ca ]
99default_ca = default_CA
100[ default_CA ]
101dir = .
102#certs = \$dir
103new_certs_dir = /tmp
104database = $KEYBASE.ca.db.index
105serial = $KEYBASE.ca.db.serial
106certificate = $KEYBASE.ca.crt
107private_key = $KEYBASE.ca.key
108default_days = 1825
109default_crl_days = 30
110default_md = md5
111preserve = no
112x509_extensions = user_cert
113policy = policy_anything
114[ policy_anything ]
115commonName = supplied
116emailAddress = supplied
117[ user_cert ]
118basicConstraints = critical,CA:false
119authorityKeyIdentifier = keyid:always
120extendedKeyUsage = clientAuth
121EOT
122 echo -n >$KEYBASE.ca.db.index
123 echo 00 >$KEYBASE.ca.db.serial
124 fi
125 if [ ! -e $KEYBASE.csr ]; then
126 if [ ! -e $KEYBASE.ca.keyconf ]; then
127 echo "vchat-keygen: generating config-file for self-signing $KEYBASE.ca.keyconf"
128 cat >$KEYBASE.ca.keyconf <<EOT
129[ req ]
130default_bits = 2048
131default_keyfile = user.key
132distinguished_name = req_distinguished_name
133string_mask = nombstr
134req_extensions = v3_req
135[ req_distinguished_name ]
136commonName = Name
137commonName_max = 64
138emailAddress = Email Address
139emailAddress_max = 40
140[ v3_req ]
141nsCertType = client
142basicConstraints = critical,CA:false
143EOT
144 fi
145 echo "vchat-keygen: generating Certificate Signing Request $KEYBASE.csr"
146 openssl req -new -config $KEYBASE.ca.keyconf -key $KEYBASE.key -out $KEYBASE.csr
147 else
148 echo "vchat-keygen: Certificate Signing Request $KEYBASE.csr exists"
149 fi
150 echo "vchat-keygen: signing certificate $KEYBASE.cert"
151 openssl ca -batch -config $KEYBASE.ca.conf -out $KEYBASE.cert -in $KEYBASE.csr
152 rm /tmp/00.pem $KEYBASE.ca.*
153 echo
154else
155 echo "vchat-keygen: certificate $KEYBASE.cert exists"
156fi
diff --git a/vchat-messages.h b/vchat-messages.h
new file mode 100755
index 0000000..08114d5
--- /dev/null
+++ b/vchat-messages.h
@@ -0,0 +1,120 @@
1/*
2 * vchat-client - alpha version
3 * vchat-messages.h - declaration of servermessage array
4 *
5 * Copyright (C) 2001 Andreas Kotes <count@flatline.de>
6 *
7 * This program is free software. It can be redistributed and/or modified,
8 * provided that this copyright notice is kept intact. This program is
9 * distributed in the hope that it will be useful, but without any warranty;
10 * without even the implied warranty of merchantability or fitness for a
11 * particular purpose. In no event shall the copyright holder be liable for
12 * any direct, indirect, incidental or special damages arising in any way out
13 * of the use of this software.
14 *
15 */
16
17/* servermessage array with structure as defined in vchat.h */
18
19servermessage servermessages[] = {
20/* 100 <message...>
21 Informational message for human consumption */
22 {"100", SM_INFO, NULL, NULL},
23/* 110 <channel> <nick> <fromhost>
24 User status summary */
25 {"110", SM_USERINFO, NULL, NULL},
26/* 111 <date> <time> <code> <message>
27 Server log information */
28 {"111", SM_INFO, NULL, NULL},
29/* 112 <information>
30 Server user information */
31 {"112", SM_USERINFO, NULL, NULL},
32/* 113 <ignore-info>
33 Ignore command confirmation */
34 {"113", SM_IGNORE, NULL, NULL},
35/* 114 <nick> changed the channel topic to <topic>
36 Channel topic change confirmation */
37 {"114", SM_CHANNEL, topicchange, NULL},
38/* 115 <channel> <topic>
39 Channel status information */
40 {"115", SM_CHANNEL, topicinfo, NULL},
41/* 116 <nick> <from> <server> <userinfo>
42 User status information */
43 {"116", SM_USERINFO, NULL, NULL},
44/* 117 <nick> <statistics>
45 User statistics information */
46 {"117", SM_USERINFO, NULL, NULL},
47/* 118 <nick> <action>
48 User action information */
49 {"118", SM_CHANNEL, pubaction, NULL},
50/* 119 <nicks>
51 Userlist */
52 {"119", SM_USERINFO, receivenicks, NULL},
53/* 122 <url>
54 URL List */
55 {"122", SM_USERINFO, NULL, NULL},
56/* 123 <user login [user logout]>
57 User Login / Logout Information */
58 {"123", SM_USERINFO, NULL, NULL},
59/* 201 <protocol> <server-name>
60 Server signon */
61 {"201", SM_IGNORE, NULL, NULL},
62/* 211 <nickname>
63 User signon */
64 {"211", SM_USERINFO, usersignon, NULL},
65/* 212 <nickname>
66 User signon (self) */
67 {"212", SM_INFO, justloggedin, NULL},
68/* 221 <nickname>
69 User signoff */
70 {"221", SM_USERINFO, usersignoff, NULL},
71/* 231 <nickname>
72 Channel departure */
73 {"231", SM_CHANNEL, userleave, NULL},
74/* 232 <nickname>
75 Channel join */
76 {"232", SM_CHANNEL, userjoin, NULL},
77/* 241 <oldnick> <newnick>
78 Nickname change */
79 {"241", SM_USERINFO, usernickchange, NULL},
80/* 120 <registered nick> */
81 {"120", SM_INFO, login, NULL},
82/* 121 You're not logged in */
83 {"121", SM_INFO, anonlogin, NULL},
84/* 301 Message truncated */
85 {"301", SM_ERROR, NULL, NULL},
86/* 302 Message too long */
87 {"302", SM_ERROR, NULL, NULL},
88/* 303 No protocol lines matched expression. */
89 {"303", SM_ERROR, NULL, NULL},
90/* 304 Already in that channel */
91 {"304", SM_ERROR, NULL, NULL},
92/* 305 Still there? */
93 {"305", SM_INFO, idleprompt, NULL},
94/* 401 Character set failure, syntax error */
95 {"401", SM_ERROR, nickerr, NULL},
96/* 402 Uninterpretible command */
97 {"402", SM_ERROR, NULL, NULL},
98/* 403 Not logged in */
99 {"403", SM_ERROR, nickerr, NULL},
100/* 404 Already logged in */
101 {"404", SM_ERROR, NULL, NULL},
102/* 405 This is already the topic */
103 {"405", SM_ERROR, NULL, NULL},
104/* 411 Nickname in use */
105 {"411", SM_ERROR, nickerr, NULL},
106/* 412 Nickname not found (private message not sent) */
107 {"412", SM_ERROR, pmnotsent, NULL},
108/* 413 Channel not found */
109 {"413", SM_ERROR, NULL, NULL},
110/* 414 Access violation */
111 {"414", SM_ERROR, NULL, NULL},
112/* 415 Nickname reserved */
113 {"415", SM_ERROR, nickerr, NULL},
114/* 501 Disconnected by own request */
115 {"501", SM_INFO, NULL, NULL},
116/* 502 Disconnected by operator */
117 {"502", SM_INFO, NULL, NULL},
118/* END OF MESSAGES */
119 {"", SM_IGNORE, NULL, NULL}
120};
diff --git a/vchat-protocol.c b/vchat-protocol.c
new file mode 100755
index 0000000..05abc26
--- /dev/null
+++ b/vchat-protocol.c
@@ -0,0 +1,1060 @@
1/*
2 * vchat-client - alpha version
3 * vchat-protocol.c - handling of server connection & messages
4 *
5 * Copyright (C) 2001 Andreas Kotes <count@flatline.de>
6 *
7 * This program is free software. It can be redistributed and/or modified,
8 * provided that this copyright notice is kept intact. This program is
9 * distributed in the hope that it will be useful, but without any warranty;
10 * without even the implied warranty of merchantability or fitness for a
11 * particular purpose. In no event shall the copyright holder be liable for
12 * any direct, indirect, incidental or special damages arising in any way out
13 * of the use of this software.
14 *
15 */
16
17/* general includes */
18#include <unistd.h>
19#include <errno.h>
20#include <stdio.h>
21#include <netdb.h>
22#include <string.h>
23#include <sys/types.h>
24#include <sys/socket.h>
25#include <netinet/in.h>
26#include <readline/readline.h>
27#include <openssl/ssl.h>
28#include <openssl/err.h>
29
30/* local includes */
31#include "vchat.h"
32
33/* version of this module */
34unsigned char *vchat_io_version = "$Id$";
35
36/* externally used variables */
37int serverfd = -1;
38unsigned int usingcert = 1;
39
40/* locally global variables */
41/* SSL-connection */
42static SSL *sslconn = NULL;
43
44/* declaration of an OpenSSL function which isn't exported by the includes,
45 * but by the library. we use it to set the accepted list of ciphers */
46STACK_OF(SSL_CIPHER) * ssl_create_cipher_list (const SSL_METHOD * meth, STACK_OF (SSL_CIPHER) ** pref, STACK_OF (SSL_CIPHER) ** sorted, const unsigned char *rule_str);
47
48static unsigned char *sslpubkey = NULL; /* servers public key extracted from X.509 certificate */
49static int sslpubkeybits = 0; /* length of server public key */
50
51/* declaration of local helper functions */
52static void usersignon (unsigned char *);
53static void usersignoff (unsigned char *);
54static void usernickchange (unsigned char *);
55static void userjoin (unsigned char *);
56static void userleave (unsigned char *);
57static void receivenicks (unsigned char *message);
58static void justloggedin (unsigned char *message);
59static void nickerr (unsigned char *message);
60static void login (unsigned char *message);
61static void anonlogin (unsigned char *message);
62static void topicinfo (unsigned char *message);
63static void pubaction (unsigned char *message);
64static void idleprompt (unsigned char *message);
65static void topicchange (unsigned char *message);
66static void pmnotsent (unsigned char *message);
67
68/* declaration of server message array */
69#include "vchat-messages.h"
70
71/* status-variable from vchat-client.c
72 * eventloop is done as long as this is true */
73extern int status;
74
75int usessl = 1;
76
77/* connects to server */
78int
79vcconnect (unsigned char *server, unsigned int port)
80{
81 /* used for tilde expansion of cert & key filenames */
82 unsigned char *tildex = NULL;
83 /* buffer for X.509 subject of server certificate */
84 unsigned char subjbuf[256];
85 /* variables used to split the subject */
86 unsigned char *subjv = NULL;
87 unsigned char *subjn = NULL;
88 unsigned char *subjc = NULL;
89 unsigned char *subjh = NULL;
90 /* pointer to key in certificate */
91 EVP_PKEY *certpubkey = NULL;
92 /* temporary result */
93 int result;
94 /* servers hostentry */
95 struct hostent *serverhe;
96 /* servers sockaddr */
97 struct sockaddr_in serversi;
98 /* SSL-context */
99 SSL_CTX *sslctx = NULL;
100 /* SSL server certificate */
101 X509 *sslserv = NULL;
102 /* SSL method function */
103 SSL_METHOD *sslmeth = NULL;
104
105 /* pointer to tilde-expanded certificate/keyfile-names */
106 unsigned char *certfile = NULL, *keyfile = NULL;
107
108 /* variable for verify return */
109 long verify;
110
111 /* get host-entry for server */
112 if ((serverhe = gethostbyname (server)) == NULL)
113 return 0;
114
115 /* get socket */
116 if ((serverfd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
117 return 0;
118
119 /* initialize datastructure for connect */
120 serversi.sin_family = AF_INET;
121 serversi.sin_port = htons (port);
122 serversi.sin_addr = *((struct in_addr *) serverhe->h_addr);
123 memset (&(serversi.sin_zero), 0, 8);
124
125 /* attempt connect */
126 if (connect (serverfd, (struct sockaddr *) &serversi, sizeof (struct sockaddr)) == -1)
127 return 0;
128
129 /* inform user */
130 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CONNECTED), server, port);
131 writechan (tmpstr);
132
133 usessl = getintoption(CF_USESSL);
134
135 /* do we want to use SSL? */
136 if (usessl)
137 {
138 /* set ssl method -> SSLv2, SSLv3 & TLSv1 client */
139 sslmeth = SSLv23_client_method ();
140 /* generate new SSL context */
141 sslctx = SSL_CTX_new (sslmeth);
142
143 /* set passphrase-callback to own function from vchat-ui.c */
144 SSL_CTX_set_default_passwd_cb (sslctx, passprompt);
145
146 /* set our list of accepted ciphers */
147 ssl_create_cipher_list (sslctx->method, &(sslctx->cipher_list), &(sslctx->cipher_list_by_id), getstroption (CF_CIPHERSUITE));
148
149 /* get name of certificate file */
150 certfile = getstroption (CF_CERTFILE);
151
152 /* do we have a certificate file? */
153 if (certfile)
154 {
155 /* does the filename start with a tilde? expand it! */
156 if (certfile[0] == '~')
157 tildex = tilde_expand (certfile);
158 else
159 tildex = certfile;
160
161 if (usingcert) {
162 /* try to load certificate */
163 result = SSL_CTX_use_certificate_file (sslctx, tildex, SSL_FILETYPE_PEM);
164 if (!result)
165 {
166 /* failed, inform user */
167 snprintf (tmpstr, TMPSTRSIZE, "!! Loading user certificate fails: %s", ERR_error_string (ERR_get_error (), NULL));
168 writecf (FS_ERR,tmpstr);
169 }
170 else
171 {
172 /* get name of key file */
173 keyfile = getstroption (CF_KEYFILE);
174
175 /* if we don't have a key file, the key may be in the cert file */
176 if (!keyfile)
177 keyfile = certfile;
178
179 /* does the filename start with a tilde? expand it! */
180 if (keyfile[0] == '~')
181 tildex = tilde_expand (keyfile);
182 else
183 tildex = keyfile;
184
185 /* try to load key (automatically asks for passphrase, if encrypted */
186 result = SSL_CTX_use_PrivateKey_file (sslctx, tildex, SSL_FILETYPE_PEM);
187 if (!result)
188 {
189 /* failed, inform user */
190 snprintf (tmpstr, TMPSTRSIZE, "!! Loading private key fails: %s", ERR_error_string (ERR_get_error (), NULL));
191 writecf (FS_ERR,tmpstr);
192 }
193 else
194 {
195 /* check if OpenSSL thinks key & cert belong together */
196 result = SSL_CTX_check_private_key (sslctx);
197 if (!result)
198 {
199 /* they don't, inform user */
200 snprintf (tmpstr, TMPSTRSIZE, "!! Verifying key and certificate fails: %s", ERR_error_string (ERR_get_error (), NULL));
201 writecf (FS_ERR,tmpstr);
202 }
203 }
204 }
205 }
206 }
207
208 /* don't worry to much about servers X.509 certificate chain */
209 SSL_CTX_set_verify_depth (sslctx, 0);
210
211 /* debug massage about verify mode */
212 snprintf (tmpstr, TMPSTRSIZE, "# Connecting with verify depth %d in mode %d", SSL_CTX_get_verify_depth (sslctx), SSL_CTX_get_verify_mode (sslctx));
213 writecf (FS_DBG,tmpstr);
214
215 /* generate new SSL connection object and associate
216 * filedescriptor of server connection */
217 sslconn = SSL_new (sslctx);
218 SSL_set_fd (sslconn, serverfd);
219
220 /* try SSL handshake */
221 if (SSL_connect (sslconn) <= 0)
222 {
223 /* no SSL connection possible */
224 snprintf (tmpstr, TMPSTRSIZE, "!! SSL Connect fails: %s", ERR_error_string (ERR_get_error (), NULL));
225 writecf (FS_ERR,tmpstr);
226 return 0;
227 }
228
229 /* debug message about used symmetric cipher */
230 snprintf (tmpstr, TMPSTRSIZE, "# SSL connection uses %s cipher (%d bits)", SSL_get_cipher_name (sslconn), SSL_get_cipher_bits (sslconn, NULL));
231 writecf (FS_DBG,tmpstr);
232
233 /* if we can't get the servers certificate something is damn wrong */
234 if (!(sslserv = SSL_get_peer_certificate (sslconn)))
235 return 0;
236
237 /* ugly dump of server certificate */
238 /* TODO: make this happen elsewhere, and preferably only when user wants
239 * or a new key needs to be added to the known_hosts */
240 writecf (FS_DBG,"# SSL Server information:");
241 /* copy X.509 to local buffer */
242 strncpy (subjbuf, sslserv->name, 256);
243 /* split a subject line and print the fullname/value pairs */
244 subjv = &subjbuf[1];
245 while (subjv)
246 {
247 subjc = strchr (subjv, '=');
248 subjc[0] = '\0';
249 subjc++;
250 /* yeah, ugly */
251 if (!strcasecmp ("C", subjv))
252 {
253 subjn = "Country ";
254 }
255 else if (!strcasecmp ("ST", subjv))
256 {
257 subjn = "State ";
258 }
259 else if (!strcasecmp ("L", subjv))
260 {
261 subjn = "Location ";
262 }
263 else if (!strcasecmp ("O", subjv))
264 {
265 subjn = "Organization";
266 }
267 else if (!strcasecmp ("OU", subjv))
268 {
269 subjn = "Organiz.unit";
270 }
271 else if (!strcasecmp ("CN", subjv))
272 {
273 subjn = "Common name ";
274 subjh = subjc;
275 }
276 else if (!strcasecmp ("Email", subjv))
277 {
278 subjn = "Emailaddress";
279 }
280 else
281 {
282 subjn = "UNKNOWN ";
283 }
284 subjv = strchr (subjc, '/');
285 if (subjv)
286 {
287 subjv[0] = '\0';
288 subjv++;
289 }
290 /* print value pair */
291 snprintf (tmpstr, TMPSTRSIZE, "# %s: %s", subjn, subjc);
292 writecf (FS_DBG,tmpstr);
293 }
294
295 /* check if verifying the server's certificate yields any errors */
296 verify = SSL_get_verify_result (sslconn);
297 if (verify)
298 {
299 /* it does - yield a warning to the user */
300 snprintf (tmpstr, TMPSTRSIZE, "!! Certificate verification fails: %s", X509_verify_cert_error_string (verify));
301 writecf (FS_ERR,tmpstr);
302 }
303
304 /* check if servers name in certificate matches the hostname we connected to */
305 if (subjh)
306 if (strcasecmp (server, subjh))
307 {
308 /* it doesn't - yield a warning and show difference */
309 writecf (FS_ERR,"!! Server name does not match name in presented certificate!");
310 snprintf (tmpstr, TMPSTRSIZE, "!! '%s' != '%s'", server, subjh);
311 writecf (FS_ERR,tmpstr);
312 }
313
314 /* try to get the servers public key */
315 certpubkey = X509_get_pubkey (sslserv);
316 if (certpubkey == NULL)
317 {
318 /* huh, we can't? */
319 writecf (FS_ERR,"!! Can't get the public key associated to the certificate");
320 }
321 /* check what type of key we've got here */
322 else if (certpubkey->type == EVP_PKEY_RSA)
323 {
324 /* RSA key, convert bignum values from OpenSSL's structures to
325 * something readable */
326 sslpubkey = BN_bn2hex (certpubkey->pkey.rsa->n);
327 sslpubkeybits = BN_num_bits (certpubkey->pkey.rsa->n);
328 /* dump keylength and hexstring of servers key to user */
329 snprintf (tmpstr, TMPSTRSIZE, "# RSA public key%s: %d %s", (certpubkey->pkey.rsa-> d) ? " (private key available)" : "", sslpubkeybits, sslpubkey);
330 writecf (FS_DBG,tmpstr);
331 /* TODO: known_hosts check here ... */
332 }
333 else if (certpubkey->type == EVP_PKEY_DSA)
334 {
335 /* Can't handle (and didn't encounter) DSA keys */
336 writecf (FS_ERR,"# DSA Public Key (output currently not supported)");
337 /* TODO: known_hosts check here ... */
338 }
339 else
340 {
341 writecf (FS_ERR,"# Public Key type not supported");
342 /* TODO: fail known_hosts check ... */
343 }
344 }
345
346 /* if we didn't fail until now, we've got a connection. */
347 return 1;
348}
349
350/* disconnect from server */
351void
352vcdisconnect ()
353{
354 close (serverfd);
355 serverfd = -1;
356}
357
358/* handle a pm not sent error
359 * format: 412 %s */
360static void
361pmnotsent (unsigned char *message)
362{
363 while(*message && *message!=' ') message++;
364 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_ERR),message+1);
365 writepriv( tmpstr);
366
367}
368
369/* parse and handle an action string
370 * format: 118 %s %s
371 * vars: %s nick
372 * %s action */
373static void
374pubaction (unsigned char *message)
375{
376 unsigned char *nick = NULL, *action = NULL;
377 nick = strchr (message, ' ');
378 nick[0] = '\0';
379 nick++;
380
381 action = strchr (nick, ' ');
382 action[0] = '\0';
383 action++;
384
385 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_PUBACTION),nick,action);
386 writechan (tmpstr);
387}
388
389/* parse and handle an idle message
390 * format: 305
391 * vars: %s message */
392static void
393idleprompt (unsigned char *message)
394{
395 unsigned char *msg = NULL;
396 msg = strchr (message, ' ');
397 msg[0] = '\0';
398 msg++;
399
400 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_IDLE),msg);
401 writechan (tmpstr);
402}
403
404/* parse and handle a topic information string
405 * format: 115 %d %s
406 * vars: %d chan - channel number
407 * %s topic - topic */
408static void
409topicinfo (unsigned char *message)
410{
411 unsigned char *channel = NULL, *topic = NULL;
412 int tmpchan = 0;
413
414 /* search start of channel number */
415 channel = strchr (message, ' ');
416 channel++;
417
418 /* search start of topic and terminate channel number */
419 topic = strchr (channel, ' ');
420 topic[0] = '\0';
421 topic++;
422
423 /* convert channel number to integer */
424 tmpchan = atoi (channel);
425
426 if (tmpchan == chan) {
427 /* show change in topic window */
428 if (strlen(topic))
429 snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), chan, topic);
430 else
431 snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_NOTOPICW), chan);
432 topicline(NULL);
433 }
434
435 /* announce change in channel window */
436 if (strlen(topic))
437 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_TOPIC), tmpchan, topic);
438 else
439 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_NOTOPIC), tmpchan);
440 writechan (tmpstr);
441}
442
443/* parse and handle a topic change string
444 * format: 114 %s changes topic to '%s'
445 * vars: %s nick
446 * %s topic */
447static void
448topicchange (unsigned char *message)
449{
450 unsigned char *nick = NULL, *topic = NULL;
451 int len;
452
453 /* search start of nickname */
454 nick = strchr (message, ' ');
455 nick++;
456
457 /* search start of message before topic, terminate nick */
458 topic = strchr (nick, ' ');
459 topic[0] = '\0';
460 topic++;
461
462 /* search start of real topic and terminate channel number */
463 topic = strchr (topic, '\'');
464 topic[0] = '\0';
465 topic++;
466
467 /* remove ending '\'', if there */
468 len = strlen (topic);
469 if (topic[len-1] == '\'')
470 topic[len-1] = '\0';
471
472 /* show change in topic window */
473 snprintf (topicstr, TOPICSTRSIZE, getformatstr(FS_TOPICW), chan, topic);
474 topicline(NULL);
475
476 /* announce change in channel window */
477 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CHGTOPIC), nick, topic);
478 writechan (tmpstr);
479}
480
481/* parse and handle a login message
482 * format: 212 %s %s
483 * vars: %s str1 - nick used to login
484 * %s str2 - servers message */
485static void
486justloggedin (unsigned char *message)
487{
488 unsigned char *str1 = NULL, *str2 = NULL;
489 /* search start of nickname */
490 str1 = strchr (message, ' ');
491 str1++;
492
493 /* search start of message, terminate nick */
494 str2 = strchr (str1, ' ');
495 str2[0] = '\0';
496 str2++;
497
498 /* if we have a new nick, store it */
499 if (!nick || strcasecmp (nick, str1))
500 setstroption(CF_NICK,str1);
501
502 /* show change in console window */
503 snprintf (consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), nick, getstroption (CF_SERVERHOST), getintoption (CF_SERVERPORT));
504 consoleline (NULL);
505
506 /* announce login as servermessage */
507 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), nick, str2);
508 writechan (tmpstr);
509
510 /* we're not logged in, change status and request nicks */
511 if (!loggedin)
512 {
513 networkoutput (".S");
514 loggedin = 1;
515 }
516}
517
518/* this user joins a different channel */
519void
520ownjoin (int channel)
521{
522 /* change global channel info */
523 chan = channel;
524 networkoutput(".t");
525 snprintf(tmpstr, TMPSTRSIZE, ".S %d",chan);
526 networkoutput(tmpstr);
527}
528
529/* this user leaves a channel */
530void
531ownleave (int channel)
532{
533 /* change global channel info */
534 chan = 0;
535}
536
537/* this user changes his nick */
538void
539ownnickchange (unsigned char *newnick)
540{
541 /* free old nick, store copy of new nick */
542 setstroption(CF_NICK,newnick);
543
544 /* show change in console window */
545 snprintf (consolestr, CONSOLESTRSIZE, getformatstr(FS_CONSOLE), nick, getstroption (CF_SERVERHOST), getintoption (CF_SERVERPORT));
546 consoleline (NULL);
547}
548
549/* parse and handle a nick error message
550 * format: 401 %s
551 * 403 %s
552 * 415 %s
553 * vars: %s - server message */
554static void
555nickerr (unsigned char *message)
556{
557 unsigned char *helpkiller = NULL;
558 /* mutate message for output */
559 message[2] = '!';
560 /* help information found? remove it. */
561 if ((helpkiller = strstr (message, " Type .h for help")))
562 helpkiller[0] = '\0';
563 /* nickchange not done? eliminate message */
564 if (loggedin && (helpkiller = strstr (message, " - Nick not changed")))
565 helpkiller[0] = '\0';
566 /* show errormessage */
567 writecf (FS_ERR,&message[2]);
568
569 /* not logged in? insist on getting a new nick */
570 if (!loggedin)
571 {
572 /* free bogus nick */
573 setstroption(CF_NICK,NULL);
574 /* get new nick via vchat-ui.c */
575 nickprompt ();
576
577 /* form login message and send it to server */
578 snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", nick, getstroption (CF_FROM), getintoption (CF_CHANNEL));
579 networkoutput (tmpstr);
580 }
581}
582
583/* parse and handle a registered nick information
584 * format: 120 %s %s
585 * vars: %s - this users registered nick
586 * %s msg - server message */
587static void
588login (unsigned char *message)
589{
590 unsigned char *msg = NULL;
591
592 /* mutate message for output */
593 message[2] = '*';
594 /* show message to user */
595 writecf (FS_SERV,&message[2]);
596
597 /* we don't know our nick? */
598 if (!nick)
599 {
600 /* find message after nick */
601 msg = strchr (&message[4], ' ');
602 if (msg)
603 {
604 /* terminate string before message and copy nick */
605 msg[0] = '\0';
606 setstroption(CF_NICK,&message[4]);
607 }
608 else
609 {
610 /* no string in servers message (huh?), ask user for nick */
611 nickprompt ();
612 }
613 }
614
615 /* form login message and send it to server */
616 snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", nick, getstroption (CF_FROM), getintoption (CF_CHANNEL));
617 networkoutput (tmpstr);
618}
619
620/* parse and handle anon login request
621 * format: 121 %s
622 * vars: %s - server message */
623static void
624anonlogin (unsigned char *message)
625{
626 /* mutate message for output */
627 message[2] = '*';
628 /* show message to user */
629 writecf (FS_SERV,&message[2]);
630
631 /* we don't know our nick? ask for it! */
632 if (!nick)
633 nickprompt ();
634
635 /* form login message and send it to server */
636 snprintf (tmpstr, TMPSTRSIZE, ".l %s %s %d", nick, getstroption (CF_FROM), getintoption (CF_CHANNEL));
637 networkoutput (tmpstr);
638}
639
640/* parse and handle list of nicks (from '.S')
641 * format: 119 %s ..
642 * vars: %s nick - a users nick */
643static void
644receivenicks (unsigned char *message)
645{
646 unsigned char *str1 = NULL, *str2 = NULL;
647 int mychan = 0;
648 void (*ul_myfunc)(unsigned char*,int);
649
650 /* show message to user */
651 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_USONLINE), &message[4]);
652 writechan (tmpstr);
653
654 /* search for channelnumber */
655 str1 = strchr (message, ' ');
656 str1++;
657 if (str1[0] == '*') {
658 if (nicks) return;
659 ul_myfunc = ul_add;
660 str1++;
661 } else {
662 str2 = str1;
663 str1 = strchr(str2,' ');
664 str1[0] = '\0';
665 mychan = atoi(str2);
666 ul_myfunc = ul_moveuser;
667 }
668 str1++;
669
670 /* while user .. */
671 while (str1)
672 {
673 /* search next user */
674 str2 = strchr (str1, ' ');
675 /* there is another user? terminate this one */
676 if (str2) {
677 str2[0] = '\0';
678 str2++;
679 }
680
681 /* add this user via vchat-user.c */
682 ul_myfunc (str1,mychan);
683
684 /* next user .. */
685 str1 = str2;
686 }
687}
688
689/* parse and handle a login message
690 * format: 211 %s %s
691 * vars: %s nick - who logged on
692 * %s msg - servers message */
693static void
694usersignon (unsigned char *message)
695{
696 unsigned char *nick = NULL, *msg = NULL;
697 /* search start of nickname */
698 nick = strchr (message, ' ');
699 nick++;
700
701 /* search start of message, terminate nick */
702 msg = strchr (nick, ' ');
703 msg[0] = '\0';
704 msg++;
705
706 /* add this user via vchat-user.c */
707 ul_add (nick, 0);
708
709 /* show message to user */
710 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNON), nick, msg);
711 writechan (tmpstr);
712}
713
714/* parse and handle a logoff message
715 * format: 221 %s %s
716 * vars: %s nick - who logged off
717 * %s msg - servers message */
718static void
719usersignoff (unsigned char *message)
720{
721 unsigned char *nick = NULL, *msg = NULL;
722 /* search start of nickname */
723 nick = strchr (message, ' ');
724 nick++;
725
726 /* search start of message, terminate nick */
727 msg = strchr (nick, ' ');
728 msg[0] = '\0';
729 msg++;
730
731 /* delete this user via vchat-user.c */
732 ul_del (nick, 0);
733
734 /* show message to user */
735 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_SIGNOFF), nick, msg);
736 writechan (tmpstr);
737}
738
739/* parse and handle a join message
740 * format: 232 %s %s %d
741 * vars: %s nick - who joined
742 * %s msg - servers message
743 * %d chan - channel joined */
744static void
745userjoin (unsigned char *message)
746{
747 unsigned char *nick = NULL, *msg = NULL, *channel = NULL;
748 int chan = 0;
749
750 /* search start of nickname */
751 nick = strchr (message, ' ');
752 nick++;
753
754 /* search start of message, terminate nick */
755 msg = strchr (nick, ' ');
756 msg[0] = '\0';
757 msg++;
758
759 /* search start of channel, terminate message */
760 channel = strrchr (msg, ' ');
761 channel[0] = '\0';
762 channel++;
763
764 /* convert channel to integer */
765 chan = atoi (channel);
766
767 /* notice channel join via vchat-user.c */
768 ul_join (nick, chan);
769
770 /* show message to user */
771 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_JOIN), nick, msg, chan);
772 writechan (tmpstr);
773}
774
775/* parse and handle a leave message
776 * format: 231 %s %s %d
777 * vars: %s nick - who left
778 * %s msg - servers message
779 * %d chan - channel joined */
780static void
781userleave (unsigned char *message)
782{
783 unsigned char *nick = NULL, *msg = NULL, *channel = NULL;
784 int chan = 0;
785
786 /* search start of nickname */
787 nick = strchr (message, ' ');
788 nick++;
789
790 /* search start of message, terminate nick */
791 msg = strchr (nick, ' ');
792 msg[0] = '\0';
793 msg++;
794
795 /* convert channel to integer */
796 channel = strrchr (msg, ' ');
797 channel[0] = '\0';
798 channel++;
799
800 /* convert channel to integer */
801 chan = atoi (channel);
802
803 /* notice channel leave via vchat-user.c */
804 ul_leave (nick, chan);
805
806 /* show message to user */
807 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_LEAVE), nick, msg, chan);
808 writechan (tmpstr);
809}
810
811/* parse and handle a nickchange message
812 * format: 241 %s %s %s
813 * vars: %s oldnick - users old nick
814 * %s newnick - users new nick
815 * %s msg - server message */
816static void
817usernickchange (unsigned char *message)
818{
819 unsigned char *oldnick = NULL, *newnick = NULL, *msg = NULL;
820
821 /* search start of old nickname */
822 oldnick = strchr (message, ' ');
823 oldnick++;
824
825 /* search start of new nickname, terminate old nick */
826 newnick = strchr (oldnick, ' ');
827 newnick[0] = '\0';
828 newnick++;
829
830 /* search start of message, terminate new nick */
831 msg = strchr (newnick, ' ');
832 msg[0] = '\0';
833 msg++;
834
835 /* notice nickchange via vchat-user.c */
836 ul_nickchange (oldnick, newnick);
837
838 /* show message to user */
839 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_NICKCHANGE), oldnick, newnick, msg);
840 writechan (tmpstr);
841}
842
843/* handle received message from server */
844static void
845parsemsg (unsigned char *message)
846{
847 unsigned char *str1, *str2;
848 int i;
849 /* message to short or starts with '<'? must be channel */
850 if (message[0] == '<')
851 {
852 str1 = &message[1];
853 str2 = strchr(str1,'>');
854 str2[0] = '\0';
855 str2++;
856 if (str2[0] == ' ') str2++;
857 if (!strncasecmp(nick,str2,strlen(nick)))
858 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_MYPUBMSG),str1,str2);
859 else
860 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPUBMSG),str1,str2);
861 writechan (tmpstr);
862 }
863 else if (message[0] == '[')
864 {
865 str1 = &message[1];
866 str2 = strchr(str1,']');
867 str2[0] = '\0';
868 str2++;
869 if (str2[0] == ' ') str2++;
870 if (!strncasecmp(nick,str2,strlen(nick)))
871 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_MYPUBURL),str1,str2);
872 else
873 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPUBURL),str1,str2);
874 writechan (tmpstr);
875 }
876 /* message starts with '*'? must be private */
877 else if (message[0] == '*')
878 {
879 str1 = &message[1];
880 str2 = strchr(str1,'*');
881 str2[0] = '\0';
882 str2++;
883 if (str2[0] == ' ') str2++;
884 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_RXPRIVMSG),str1,str2);
885 writepriv (tmpstr);
886 ul_msgfrom(str1);
887 }
888 /* message starts with a number? must be a servermessage */
889 else if ((message[0] >= '0') && (message[0] <= '9'))
890 {
891 /* walk server message array */
892 for (i = 0; servermessages[i].id[0]; i++)
893 {
894 /* is this the message? */
895 if (!(strncmp (servermessages[i].id, message, 3)))
896 {
897 /* scripting hook available - call it */
898 if (servermessages[i].hook)
899 servermessages[i].hook (message);
900 /* function available - call it */
901 else if (servermessages[i].funct)
902 servermessages[i].funct (message);
903 /* no function available, but known - give output */
904 else
905 {
906 /* remove continutation mark */
907 if (message[3] == '-')
908 message[3] = ' ';
909
910 /* mutate message for display */
911 message[2] = '*';
912 /* giveout message by type */
913 switch (servermessages[i].type)
914 {
915 case SM_IGNORE:
916 break;
917 case SM_INFO:
918 /* change marker and send as servermessage */
919 message[2] = '#';
920 writecf (FS_SERV,&message[2]);
921 break;
922 case SM_USERINFO:
923 /* keep marker and send as servermessage */
924 writecf (FS_SERV,&message[2]);
925 break;
926 case SM_CHANNEL:
927 /* keep marker and send as channelmessage */
928 writechan (&message[2]);
929 break;
930 case SM_ERROR:
931 /* change marker and send as errormessage */
932 message[2] = '!';
933 writecf (FS_ERR,&message[2]);
934 break;
935 default:
936 /* fallback: keep marker and send as servermessage */
937 writecf (FS_SERV,&message[2]);
938 }
939 }
940 return;
941 }
942 }
943 /* message not in list, raise errormessage */
944 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_UNKNOWNMSG), message);
945 writecf (FS_ERR,tmpstr);
946 }
947 else
948 {
949 /* message neither public, private or server, raise errormessage */
950 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_BOGUSMSG), message);
951 writecf (FS_ERR,tmpstr);
952 }
953}
954
955/* offset in buffer (for linebreaks at packet borders) */
956static int bufoff = 0;
957
958/* get data from servers filedescriptor */
959void
960networkinput (void)
961{
962 int bytes;
963 unsigned char *tmp = NULL;
964#define BUFSIZE 4096
965 unsigned char buf[BUFSIZE]; /* data buffer */
966 unsigned char *ltmp = buf;
967 buf[BUFSIZE-1] = '\0'; /* sanity stop */
968
969 /* check if we use ssl or if we don't and receive data at offset */
970 if (!usessl)
971 bytes = recv (serverfd, &buf[bufoff], BUFSIZE-1 - bufoff, 0);
972 else
973 bytes = SSL_read (sslconn, &buf[bufoff], BUFSIZE-1 - bufoff);
974
975 /* no bytes transferred? raise error message, bail out */
976 if (bytes < 0)
977 {
978 snprintf (tmpstr, TMPSTRSIZE, "Receive fails, %s.", sys_errlist[errno]);
979 strncpy(errstr,tmpstr,TMPSTRSIZE-2);
980 errstr[TMPSTRSIZE-2] = '\0';
981 strcat(errstr,"\n");
982 writecf (FS_ERR,tmpstr);
983 status = 0;
984 }
985 /* end of file from server? */
986 else if (bytes == 0)
987 {
988 /* inform user, bail out */
989 writecf (FS_SERV,"* EOF from server");
990 strncpy(errstr,"* EOF from server",TMPSTRSIZE-2);
991 errstr[TMPSTRSIZE-2] = '\0';
992 strcat(errstr,"\n");
993 status = 0;
994 }
995 else
996 {
997 /* terminate message */
998 buf[bytes + bufoff] = '\0';
999 /* as long as there are lines .. */
1000 while ((tmp = strchr (ltmp, '\n')) != NULL)
1001 {
1002 /* did the server send CR+LF instead of LF with the last line? */
1003 if (tmp[-1] == '\r')
1004 tmp[-1] = '\0';
1005
1006 /* remove newline from previous message, advance pointer of next
1007 * message */
1008 tmp[0] = '\0';
1009 tmp++;
1010
1011 /* we have a last message? give away to line handler! */
1012 if (ltmp[0])
1013 {
1014#ifdef DEBUG
1015 /* debugging? log network input! */
1016 fprintf (stderr, "<| %s\n", ltmp);
1017#endif
1018 parsemsg (ltmp);
1019 }
1020
1021 /* move line along .. */
1022 ltmp = tmp;
1023 }
1024 /* buffer exhausted, move partial line to start of buffer and go on .. */
1025 bufoff = (bytes+bufoff) - (ltmp-buf);
1026 if (bufoff > 0)
1027 memmove (buf, ltmp, bufoff);
1028 else
1029 bufoff = 0;
1030 }
1031}
1032
1033void
1034networkoutput (unsigned char *msg)
1035{
1036#ifdef DEBUG
1037 /* debugging? log network output! */
1038 fprintf (stderr, ">| %s\n", msg);
1039#endif
1040
1041 /* TODO: err .. rework this (blocking) code */
1042
1043 /* check if we use ssl or if we don't and send data to server */
1044 if (!usessl) {
1045 if (send (serverfd, msg, strlen (msg), 0) != strlen (msg)) {
1046 writecf (FS_ERR,"Message sending fuzzy.");
1047 }
1048 } else if (SSL_write (sslconn, msg, strlen (msg)) != strlen (msg)) {
1049 writecf (FS_ERR,"Message sending fuzzy.");
1050 }
1051
1052 /* check if we use ssl or if we don't and send line termination to server */
1053 if (!usessl) {
1054 if (send (serverfd, "\r\n", 2, 0) != 2) {
1055 writecf (FS_ERR,"Message sending fuzzy!");
1056 }
1057 } else if (SSL_write (sslconn, "\r\n", 2) != 2) {
1058 writecf (FS_ERR,"Message sending fuzzy.");
1059 }
1060}
diff --git a/vchat-ui.c b/vchat-ui.c
new file mode 100755
index 0000000..e9368b4
--- /dev/null
+++ b/vchat-ui.c
@@ -0,0 +1,1504 @@
1/*
2 * vchat-client - alpha version
3 * vchat-ui.c - user-interface and readline handling
4 *
5 * Copyright (C) 2001 Andreas Kotes <count@flatline.de>
6 *
7 * This program is free software. It can be redistributed and/or modified,
8 * provided that this copyright notice is kept intact. This program is
9 * distributed in the hope that it will be useful, but without any warranty;
10 * without even the implied warranty of merchantability or fitness for a
11 * particular purpose. In no event shall the copyright holder be liable for
12 * any direct, indirect, incidental or special damages arising in any way out
13 * of the use of this software.
14 *
15 */
16
17/* general includes */
18#include <unistd.h>
19#include <ncurses.h>
20#include <signal.h>
21#include <stdlib.h>
22#include <errno.h>
23#include <termios.h>
24#include <sys/ioctl.h>
25#include <readline/readline.h>
26#include <readline/history.h>
27#include <openssl/pem.h>
28#include <regex.h>
29#include "vchat.h"
30
31/* version of this module */
32unsigned char *vchat_ui_version = "$Id$";
33
34/* externally used variables */
35/* current string in topic window */
36unsigned char topicstr[TOPICSTRSIZE] = "[] VChat 0.16";
37/* current string in console window */
38unsigned char consolestr[CONSOLESTRSIZE] = "[ Get help: .h for server /h for client commands";
39
40static unsigned int ui_init = 0;
41
42/* our windows */
43static WINDOW *console = NULL;
44static WINDOW *input = NULL;
45static WINDOW *topic = NULL;
46static WINDOW *channel = NULL;
47static WINDOW *private = NULL;
48static WINDOW *output = NULL;
49/* our screen dimensions */
50static int screensx = 0;
51static int screensy = 0;
52/* length of last input on line (for clearing) */
53static int lastlen = 0;
54/* current horizontal scrolling offset for input line */
55static int scroff = 0;
56/* cache for stepping value of horizontal scrolling */
57unsigned int hscroll = 0;
58
59static int outputshown = 0;
60static int outputwidth_desired = 0;
61
62static int privheight = 0;
63static int privheight_desired = 0;
64static int privwinhidden = 0;
65int usetime = 1;
66int outputcountdown = 0;
67
68struct sb_entry {
69 int id;
70 time_t when;
71 int stamp;
72 unsigned char *what;
73 struct sb_entry *link;
74};
75
76struct sb_data {
77 struct sb_entry *entries;
78 int count;
79 int scroll;
80};
81
82static struct sb_data *sb_pub = NULL;
83static struct sb_data *sb_priv = NULL;
84static struct sb_data *sb_out = NULL;
85
86/* Tells, which window is active */
87static int sb_win = 0; /* 0 for pub, 1 for priv */
88
89/* struct to keep filter list */
90struct filt {
91 unsigned char colour;
92 unsigned int id;
93 unsigned char *text;
94 regex_t regex;
95 struct filt *next;
96};
97
98typedef struct filt filt;
99
100static filt *filterlist = NULL;
101static int filtertype = 0;
102static int currentstamp = 0;
103
104/* Prototype declarations */
105
106static void resize (int);
107static void drawwin (WINDOW *win, struct sb_data *sb);
108static int writescr (WINDOW *win, struct sb_entry *entry);
109static int testfilter ( struct sb_entry *entry);
110static int gettextwidth (char *textbuffer);
111static void resize_output (void);
112static int getsbeheight (struct sb_entry *entry, const int xwidth, int needstime );
113static int getsbdataheight (struct sb_data *data, const int xwidth, int needstime );
114/* CURRENTLY UNUSED
115static void writecolorized (WINDOW *win, unsigned char *string);
116*/
117
118enum {
119 RMFILTER_RMANDCONT,
120 RMFILTER_RMANDSTOP,
121 RMFILTER_KEEPANDCONT,
122 RMFILTER_KEEPANDSTOP
123};
124
125/* readlines callback when a line is completed */
126static void
127linecomplete (unsigned char *line)
128{
129 int i;
130
131 /* send linefeed, return pointer, reset cursors */
132 waddch (input, '\n');
133 wmove (input, 0, 0);
134 rl_point = 0;
135 scroff = 0;
136
137 if (line) {
138 i = strlen(line)-1;
139 while (line[i] == ' ') line[i--]='\0';
140
141 if (line[0] && strchr(line,' ') == NULL && line[i] == ':')
142 line[i--] = '\0';
143
144 /* empty line? nada. */
145 if (!line[0])
146 return;
147
148 /* add line to history and have it handled in vchat-protocol.c */
149 add_history (line);
150 handleline (line);
151 free (line);
152
153 /* wipe input line and reset cursor */
154 wmove (input, 0, 0);
155 for (i = 0; i < input->_maxx; i++)
156 waddch (input, ' ');
157 wmove (input, 0, 0);
158 wrefresh (input);
159 }
160}
161
162/* redraw-callback for readline */
163static void
164vciredraw (void)
165{
166 int i;
167 /* hscroll value cache set up? */
168 if (!hscroll)
169 {
170 /* check config-option or set hardcoded default */
171 hscroll = getintoption (CF_HSCROLL);
172 if (!hscroll)
173 hscroll = 5;
174 }
175
176 /* calculate horizontal scrolling offset */
177 if (rl_point - scroff < 0)
178 scroff = rl_point - 4;
179 if (rl_point - scroff > input->_maxx)
180 scroff = rl_point - input->_maxx + 2;
181 if (rl_point - scroff > input->_maxx - (hscroll - 2))
182 scroff += hscroll;
183 else if (rl_point - scroff < input->_maxx - (hscroll + 2))
184 scroff -= hscroll;
185 if (scroff < 0)
186 scroff = 0;
187
188 /* wipe input line */
189 wmove (input, 0, 0);
190 for (i = 0; i < input->_maxx; i++)
191 waddch (input, ' ');
192
193 /* show current line, move cursor, redraw! */
194 mvwaddnstr (input, 0, 0, &rl_line_buffer[scroff], input->_maxx);
195 wmove (input, 0, rl_point - scroff);
196 wrefresh (input);
197}
198
199/* called by the eventloop in vchat-client.c */
200void
201userinput (void)
202{
203 /* let readline handle what the user typed .. */
204 rl_callback_read_char ();
205}
206
207static int
208calcdrawcus (unsigned char * const str) {
209 unsigned char *tmp = str;
210 int zero = 0;
211 while( *tmp && (*tmp!=' ') && (*tmp!='\n')) { if(*tmp==1) zero+=2; tmp++; }
212 return (tmp - str) - zero;
213}
214
215static void
216sb_flush ( struct sb_data *sb ) {
217 struct sb_entry *now = sb->entries, *prev = NULL, *tmp;
218 while( now ) {
219 tmp = (struct sb_entry*)((unsigned long)prev ^ (unsigned long)now->link);
220 free(now->what );
221 free(now);
222 prev = now;
223 now = tmp;
224 }
225 sb->entries = NULL;
226}
227
228static void
229sb_clear ( struct sb_data **sb ) {
230 sb_flush(*sb);
231 free( *sb );
232 *sb = NULL;
233}
234
235static struct sb_entry*
236sb_add (struct sb_data *sb, unsigned char *line, time_t when) {
237 struct sb_entry *newone = malloc (sizeof(struct sb_entry));
238 if( newone ) {
239 if( sb->count == sb->scroll ) sb->scroll++;
240 newone->id = sb->count++;
241 newone->when = when;
242 newone->what = strdup(line);
243 newone->link = sb->entries;
244 newone->stamp= 0xffff;
245 if( sb->entries ) sb->entries->link = (struct sb_entry*)((unsigned long)sb->entries->link ^ (unsigned long)newone);
246 sb->entries = newone;
247 }
248 return newone;
249}
250
251void flushout ( )
252{
253 sb_flush(sb_out);
254 writeout(" ");
255 outputwidth_desired = 0;
256 outputshown = 0;
257}
258
259void hideout( )
260{
261 outputshown = 0;
262 resize(0);
263}
264
265void showout (void)
266{
267 writeout(" ");
268 outputcountdown = 6;
269 outputshown = 1;
270 resize(0);
271}
272
273void writeout (unsigned char *str)
274{
275 sb_add(sb_out,str,time(NULL));
276 int i = 1 + gettextwidth( str );
277 if( i > outputwidth_desired ) outputwidth_desired = i;
278}
279
280int writechan (unsigned char *str) {
281 struct sb_entry *tmp;
282 int i = 0;
283 time_t now = time(NULL);
284 tmp = sb_add(sb_pub,str,now);
285
286 if ( (sb_pub->scroll == sb_pub->count) && ((filtertype == 0) || ( testfilter(tmp)))) {
287 i = writescr(channel, tmp);
288 wnoutrefresh(channel);
289 }
290 consoleline(NULL);
291 return i;
292}
293
294int writecf (formtstr id,unsigned char *str) {
295 struct sb_entry *tmp;
296 int i = 0;
297 time_t now = time(NULL);
298 snprintf(tmpstr,TMPSTRSIZE,getformatstr(id),str);
299 tmp = sb_add(sb_pub,tmpstr,now);
300 if ( (sb_pub->scroll == sb_pub->count) &&
301 ((filtertype == 0) || ( testfilter(tmp)))) {
302 i = writescr(channel, tmp);
303 wnoutrefresh(channel);
304 }
305 consoleline(NULL);
306 return i;
307}
308
309int writepriv (unsigned char *str) {
310 int i = 0;
311 time_t now = time (NULL);
312 struct sb_entry *tmp;
313
314 if (private) {
315 tmp = sb_add(sb_priv,str,now);
316 if ( (sb_priv->scroll == sb_priv->scroll) &&
317 ((filtertype == 0) || ( testfilter(tmp)))) {
318 i = writescr(private, tmp);
319 }
320 if( privwinhidden ) {
321 privheight_desired = privwinhidden;
322 privwinhidden = 0;
323 resize(0);
324 }
325 wnoutrefresh(private);
326 topicline(NULL);
327 } else
328 i = writechan( str );
329
330 return i;
331}
332
333/* Get #if 's out of code */
334
335#if NCURSES_VERSION_MAJOR >= 5
336
337typedef struct {
338 attr_t attr;
339 short pair;
340} ncurs_attr;
341
342#define WATTR_GET( win, orgattr ) wattr_get( win, &orgattr.attr, &orgattr.pair, NULL)
343#define WATTR_SET( win, orgattr ) wattr_set( win, orgattr.attr, orgattr.pair, NULL)
344#define BCOLR_SET( attr, colour ) attr->pair = colour;
345
346#else
347
348typedef struct {
349 attr_t attr;
350} ncurs_attr;
351
352#define WATTR_GET( win, orgattr ) orgattr.attr = wattr_get(win);
353#define WATTR_SET( win, orgattr ) wattr_set( win, orgattr.attr);
354#define BCOLR_SET( attr, colour ) attr->attr = ((attr->attr) & ~A_COLOR) | COLOR_PAIR((colour));
355
356#endif
357
358/* 'A' - 'Z' attriute type */
359static int attributes[] = { A_ALTCHARSET, A_BOLD, 0, A_DIM, 0, 0, 0, 0, A_INVIS, 0, 0, A_BLINK,
360 0, A_NORMAL, 0, A_PROTECT, 0, A_REVERSE, A_STANDOUT, 0, A_UNDERLINE,
361 0, 0, 1, 0, 0 };
362
363static void
364docolorize (unsigned char colour, ncurs_attr *attr, ncurs_attr orgattr) {
365 if( colour== '0') {
366 *attr = orgattr;
367 } else if( ( colour > '0') && ( colour <= '9')) {
368 BCOLR_SET( attr, colour - '0' );
369 } else {
370 unsigned char upc = colour & ( 0x20 ^ 0xff ); /* colour AND NOT 0x20 */
371 attr_t newattr;
372 if( ( upc >= 'A') && ( upc<='Z' ) && ( newattr = attributes[upc - 'A']) )
373 attr->attr = ( colour & 0x20 ) ? attr->attr | newattr : attr->attr & ~newattr;
374 }
375}
376
377/* draw arbitrary strings */
378static int
379writescr ( WINDOW *win, struct sb_entry *entry ) {
380 unsigned char tmp [64];
381 int charcount = 0;
382 int i;
383 int textlen = strlen( entry->what );
384 int timelen = ((win == channel)||(win == private)) && usetime ?
385 (int)strftime(tmp,64,getformatstr(FS_TIME),localtime(&entry->when)) : 0;
386 unsigned char textbuffer[ textlen+timelen+1 ];
387 ncurs_attr attrbuffer[ textlen+timelen+1 ];
388 ncurs_attr orgattr;
389
390 /* illegal window? return */
391 if( !win || !(textlen+timelen)) return 0;
392
393 /* store original attributes */
394 WATTR_GET( win, orgattr);
395 attrbuffer[ 0 ] = orgattr;
396
397 /* copy time string */
398 for( i = 0; i < timelen; i++ )
399 if( tmp[ i ] == 1 ) {
400 docolorize( tmp[++i], attrbuffer+charcount, orgattr);
401 } else {
402 attrbuffer[ charcount+1 ] = attrbuffer[ charcount ];
403 textbuffer[ charcount++ ] = tmp[ i ];
404 }
405
406 timelen = charcount;
407
408 /* copy text */
409 for( i = 0; i< textlen; i++ )
410 if( entry->what[ i ] == 1 ) {
411 docolorize( entry->what[++i], attrbuffer+charcount, orgattr);
412 } else {
413 attrbuffer[ charcount+1 ] = attrbuffer[ charcount ];
414 textbuffer[ charcount++ ] = entry->what[ i ];
415 }
416
417 /* zero terminate text --- very important :) */
418 textbuffer[ charcount ] = 0;
419
420 /* hilite */
421 if((win == channel)||(win == private)) { /* do not higlight bars */
422 filt *flt = filterlist;
423 unsigned char *instr = textbuffer;
424 regmatch_t match;
425 int j;
426
427 while( flt ) {
428 if ( (flt->colour != '-') && (flt->colour != '+')) {
429 i = timelen;
430 while( i < charcount ) {
431 if( regexec( &flt->regex, instr+i, 1, &match, 0 )) {
432 i = charcount;
433 } else {
434 for( j = i + match.rm_so; j < i + match.rm_eo; j++)
435 docolorize( flt->colour, attrbuffer+j, orgattr );
436 i += 1 + match.rm_so;
437 }
438 }
439 }
440 flt = flt->next;
441 }
442 }
443
444 if (win->_curx) waddch(win,'\n');
445
446 for( i = 0; i < charcount; i++ ) {
447 /* on start of line or attribute changes set new attribute */
448 if( !i || memcmp( attrbuffer+i, attrbuffer+i-1, sizeof(ncurs_attr)))
449 WATTR_SET( win, attrbuffer[i]);
450 if( textbuffer[ i ] == ' ') {
451 if ((calcdrawcus(textbuffer+i+1) + win->_curx > win->_maxx - 1)&&
452 (calcdrawcus(textbuffer+i+1) < win->_maxx)) {
453 /* line wrap found */
454 WATTR_SET( win, orgattr);
455 waddstr( win, "\n ");
456 WATTR_SET( win, attrbuffer[ i ]);
457 }
458 }
459 /* plot character */
460 waddch( win, textbuffer[ i ]);
461 }
462
463 /* restore old attributes */
464 WATTR_SET (win, orgattr);
465
466 return charcount;
467}
468
469static void
470resize_output ( )
471{
472 int outputwidth = (outputwidth_desired + 7 > screensx) ? screensx - 7 : outputwidth_desired;
473 int outputheight = getsbdataheight(sb_out, outputwidth-1, 0);
474
475 if (outputheight + 5 > screensy ) outputheight = screensy - 5;
476 wresize(output,outputheight,outputwidth);
477 mvwin(output,(screensy-outputheight)>>1,(screensx-outputwidth)>>1);
478 drawwin(output, sb_out);
479}
480
481static void
482doscroll( int up ) {
483 WINDOW *destwin = (sb_win && private) ? private : channel;
484 struct sb_data *sb = (sb_win && private) ? sb_priv : sb_pub;
485 struct sb_entry *now = sb->entries, *prev = NULL, *tmp;
486 int lines = destwin->_maxy>>1;
487
488 if( sb->scroll != sb->count )
489 while( now && (now->id != sb->scroll) ) {
490 tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp;
491 }
492
493 if( !up ) {
494 prev = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev);
495 tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp;
496 }
497
498 while( now && (lines > 0)) {
499 if ( (!filtertype) || ( (now->stamp != currentstamp) && ( (now->stamp == (currentstamp | (1<<15))) || testfilter( now ) ) ) )
500 {
501 lines -= getsbeheight( now, destwin->_maxx, usetime );
502 }
503 tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp;
504 }
505 if( now )
506 sb->scroll = now->id;
507 else
508 if( !up ) sb->scroll = sb->count;
509
510 drawwin(destwin, sb);
511 wnoutrefresh(destwin);
512 if( sb_win && private ) topicline(NULL); else consoleline(NULL);
513}
514
515
516void
517scrollup (void)
518{
519 doscroll( 1 );
520}
521
522void
523scrolldown (void)
524{
525 doscroll( 0 );
526}
527
528void
529scrollwin (vod)
530{
531 if (!sb_win && private) sb_win = 1;
532 else sb_win = 0;
533 topicline(NULL);
534 consoleline(NULL);
535}
536
537void
538growprivwin (vod) {
539 if( private ) {
540 if( privwinhidden)
541 privwinhidden = 0;
542 if( ++privheight_desired > screensy - 5)
543 privheight_desired = screensy - 5;
544 resize(0);
545 }
546}
547
548void toggleprivwin (vod) {
549 if( outputshown ) {
550 outputshown = 0;
551 resize(0);
552 } else {
553 if( private ) {
554 if( privwinhidden ) {
555 privheight_desired = privwinhidden;
556 privwinhidden = 0;
557 } else {
558 privwinhidden = privheight_desired;
559 privheight_desired = 1;
560 }
561 resize(0);
562 }
563 }
564}
565
566void
567shrinkprivwin (vod) {
568 if( private && !privwinhidden ) {
569 if( --privheight_desired < 1) privheight_desired = 1;
570 if( privheight_desired > screensy - 5) privheight_desired = screensy - 5;
571 resize(0);
572 }
573}
574
575/* nick completion callback for readline */
576static char **
577vcccomplete (char *text, int start, int end)
578{
579 char **matches;
580 matches = (char **) NULL;
581 /* are we at start of line, with no characters typed? message completion */
582 if (start == 0 && end == 0)
583 {
584#ifdef OLDREADLINE
585 matches = completion_matches (text, (CPFunction *) ul_mnickcomp);
586#else
587 matches = rl_completion_matches (text, (rl_compentry_func_t *) ul_mnickcomp);
588#endif
589 rl_attempted_completion_over = 1;
590 }
591 /* start of line? get matches for channel! */
592 else if (start == 0)
593 {
594#ifdef OLDREADLINE
595 matches = completion_matches (text, (CPFunction *) ul_cnickcomp);
596#else
597 matches = rl_completion_matches (text, (rl_compentry_func_t *) ul_cnickcomp);
598#endif
599 /* no, we want no 'normal' completion if started typing on the beginning
600 * of the line */
601 rl_attempted_completion_over = 1;
602 }
603 return (matches);
604}
605
606/* clear message window */
607void
608clearpriv ()
609{
610 WINDOW *dest = NULL;
611 /* do we have a private window? */
612 if (private)
613 dest = private;
614 else
615 dest = channel;
616
617 /* clear window, move cursor to bottom, redraw */
618 wclear (dest);
619 wmove (dest, dest->_maxy, dest->_maxx);
620 wrefresh (dest);
621}
622
623/* clear channel window */
624void
625clearchan ()
626{
627 /* clear window, move cursor to bottom, redraw */
628 wclear (channel);
629 wmove (channel, channel->_maxy, channel->_maxx);
630 wrefresh (channel);
631}
632
633/* Get window size */
634
635void ttgtsz(int *x,int *y) {
636#ifdef TIOCGSIZE
637 struct ttysize getit;
638#else
639#ifdef TIOCGWINSZ
640 struct winsize getit;
641#endif
642#endif
643
644 *x=0; *y=0;
645#ifdef TIOCGSIZE
646 if(ioctl(1,TIOCGSIZE,&getit)!= -1) {
647 *x=getit.ts_cols;
648 *y=getit.ts_lines;
649 }
650#else
651#ifdef TIOCGWINSZ
652 if(ioctl(1,TIOCGWINSZ,&getit)!= -1) {
653 *x=getit.ws_col;
654 *y=getit.ws_row;
655 }
656#endif
657#endif
658 }
659
660static void
661redraw (void)
662{
663 sb_pub->scroll = sb_pub->count;
664 sb_priv->scroll = sb_priv->count;
665
666 resize(0);
667}
668
669/* resize display on SIGWINCH */
670void
671resize (int signal)
672{
673 int xsize,ysize;
674 //errmsg ("! SIGWINCH raised without code for it, hope you didn't make it smaller ;)");
675 //endwin();
676 ttgtsz(&xsize,&ysize);
677 resizeterm(ysize,xsize);
678 //refresh();
679 /* store screen-dimensions to local functions */
680 getmaxyx (stdscr, screensy, screensx);
681
682 if (!privheight_desired) privheight_desired = getintoption(CF_PRIVHEIGHT);
683 if ( privheight_desired > screensy - 5) privheight = screensy - 5; else privheight = privheight_desired;
684
685 /* check dimensions or bump user */
686 if (screensy - privheight < 4)
687 {
688 fprintf (stderr, "vchat-client: screen to short (only %d rows, at least %d expected), bailing out.\n", screensy, privheight + 6);
689 snprintf (errstr, ERRSTRSIZE, "vchat-client: screen to short (only %d rows, at least %d expected), bailing out.\n", screensy, privheight + 6);
690 cleanup (0);
691 }
692 if (screensx < 14)
693 {
694 fprintf (stderr, "vchat-client: screen to thin (only %d cols, at least %d expected), bailing out.\n", screensx, 14);
695 snprintf (errstr, ERRSTRSIZE, "vchat-client: screen to thin (only %d cols, at least %d expected), bailing out.\n", screensx, 14);
696 cleanup (0);
697 }
698
699 wresize(console,1,screensx);
700 wresize(input,1,screensx);
701 if (private)
702 wresize(private,privheight,screensx);
703 wresize(topic,1,screensx);
704 wresize(channel,screensy-(privheight+3),screensx);
705
706 mvwin(console,screensy-2,0);
707 mvwin(input,screensy-1,0);
708 if(private) mvwin(private,0,0);
709 mvwin(topic,privheight,0);
710 mvwin(channel,privheight+1,0);
711
712 drawwin(channel, sb_pub);
713 if(private) drawwin(private, sb_priv);
714 wnoutrefresh(channel);
715 if(private) wnoutrefresh(private);
716
717 if(outputshown) resize_output();
718 topicline(NULL);
719 consoleline(NULL);
720 if(loggedin) vciredraw();
721}
722
723static int
724gettextwidth (char *textbuffer)
725{
726 int width = 0;
727
728 do switch( *(textbuffer++) ) {
729 case 1:
730 if (!*(textbuffer++)) return width;
731 break;
732 case 0:
733 return width;
734 break;
735 default:
736 width++;
737 break;
738 } while( 1 );
739}
740
741static int
742getsbdataheight (struct sb_data *data, const int xwidth, int needstime )
743{
744 struct sb_entry *now = data->entries, *prev = NULL, *tmp;
745 int height = 0;
746 while( now ) {
747 height += getsbeheight( now, xwidth, needstime);
748 tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp;
749 }
750 return height;
751}
752
753static int
754getsbeheight (struct sb_entry *entry, const int xwidth, int needstime )
755{
756 int curx = 0, lines = 1;
757 char tmp[ 64 ], *textbuffer;
758
759 if( needstime ) {
760 int timelen = (int)strftime(tmp,64,getformatstr(FS_TIME),localtime(&entry->when));
761 tmp[ timelen ] = 2;
762 textbuffer=tmp;
763 } else {
764 textbuffer = entry->what;
765 }
766
767 do switch( *textbuffer++ ) {
768 case ' ':
769 if ((calcdrawcus(textbuffer) + curx > xwidth - 1) &&
770 (calcdrawcus(textbuffer) < xwidth)) {
771 lines++; curx = 4;
772 } else {
773 if( curx++ == xwidth ) {
774 curx = 0; lines++;
775 }
776 }
777 break;
778 case 1:
779 if (!*textbuffer++) return lines;
780 break;
781 case 0:
782 return lines;
783 break;
784 case 2:
785 textbuffer=entry->what;
786 break;
787 default:
788 if( curx++ == xwidth ) {
789 curx = 0; lines++;
790 }
791 break;
792 } while( 1 );
793
794}
795
796static int
797analyzefilters( void ) {
798 filt *filters = filterlist;
799 int type = 0;
800
801 sb_pub->scroll = sb_pub->count;
802 sb_priv->scroll = sb_priv->count;
803
804 if( ++currentstamp == 0x3fff ) currentstamp = 1;
805
806 while( (type!=1) && filters ) {
807 if( filters->colour == '-' ) type = 2;
808 if( filters->colour == '+' ) type = 1;
809 filters=filters->next;
810 }
811 return type;
812}
813
814static int
815testfilter ( struct sb_entry* entry ) {
816 int match = 0;
817 filt *filters = filterlist;
818 char filtercolour = filtertype == 2 ? '-' : '+';
819
820 while( !match && filters ) {
821 if( filters->colour == filtercolour )
822 match = regexec( &filters->regex, entry->what, 0, NULL, 0 ) ? 0 : 1;
823 filters=filters->next;
824 }
825 match = ( filtertype == 2 ) ? ( 1 - match ) : match;
826 entry->stamp = (match << 15) | currentstamp;
827
828 return match;
829}
830
831static void
832drawwin (WINDOW *win, struct sb_data *sb )
833{
834 if (win) {
835 struct sb_entry *now = sb->entries, *prev = NULL, *tmp;
836 struct sb_entry *vis[win->_maxy + 1];
837 int sumlines = 0, sumbuffers = 0;
838
839 /* public scrollback */
840 if( sb->scroll != sb->count )
841 while( now && (now->id != sb->scroll) ) {
842 tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp;
843 }
844
845 if( (win == output) || (filtertype == 0)) {
846 while( now && (sumlines <= win->_maxy )) {
847 sumlines += getsbeheight( now, win->_maxx, ((win == channel)||(win == private)) && usetime );
848 vis[ sumbuffers++ ] = now;
849 tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp;
850 }
851 } else {
852 while( now && (sumlines <= win->_maxy )) {
853 if ( (now->stamp != currentstamp) &&
854 ( (now->stamp == (currentstamp | (1<<15))) || testfilter( now ) )
855 )
856 {
857 sumlines += getsbeheight( now, win->_maxx, ((win == channel)||(win == private)) && usetime );
858 vis[ sumbuffers++ ] = now;
859 }
860 tmp = now; now = (struct sb_entry*)((unsigned long)now->link ^ (unsigned long)prev); prev = tmp;
861 }
862 }
863
864 /* If buffer is not completely filled, clear window */
865 if( sumlines < win->_maxy + 1 )
866 wclear(win);
867
868 /* Move pointer to bottom to let curses scroll */
869 wmove(win, win->_maxy, win->_maxx);
870
871 /* Plot visible lines */
872 while (sumbuffers--) writescr( win, vis[sumbuffers] );
873 }
874}
875
876#ifdef OLDREADLINE
877typedef int rl_command_func_t __P((int, int));
878#endif
879
880/* initialize curses and display */
881void
882initui (void)
883{
884 Keymap keymap;
885
886 /* init curses */
887 if (!ui_init) {
888 initscr ();
889 ui_init = 1;
890 }
891
892 /* install signalhandler */
893
894 signal (SIGWINCH, resize);
895
896 /* set options */
897 keypad (stdscr, TRUE);
898 nonl ();
899 cbreak ();
900
901 /* color or monochome display? */
902 if (has_colors ())
903 {
904 /* start color and set a colorset */
905 start_color ();
906 use_default_colors();
907 init_pair (1, COLOR_RED, -1);
908 init_pair (2, COLOR_GREEN, -1);
909 init_pair (3, COLOR_YELLOW, -1);
910 init_pair (4, COLOR_BLUE, -1);
911 init_pair (5, COLOR_MAGENTA, -1);
912 init_pair (6, COLOR_CYAN, -1);
913 init_pair (7, COLOR_WHITE, -1);
914 init_pair (8, COLOR_WHITE, COLOR_RED);
915 init_pair (9, COLOR_WHITE, COLOR_BLUE);
916 }
917 else
918 {
919 /* monochrome, start color and set a colorset anyways */
920 start_color ();
921 init_pair (1, -1, -1);
922 init_pair (2, -1, -1);
923 init_pair (3, -1, -1);
924 init_pair (4, -1, -1);
925 init_pair (5, -1, -1);
926 init_pair (6, -1, -1);
927 init_pair (7, -1, -1);
928 init_pair (8, -1, -1);
929 init_pair (9, -1, -1);
930 }
931
932 /* store screen-dimensions to local functions */
933 getmaxyx (stdscr, screensy, screensx);
934
935 if (!privheight_desired) privheight_desired = getintoption(CF_PRIVHEIGHT);
936 if ( privheight_desired > screensy - 5) privheight = screensy - 5; else privheight = privheight_desired;
937
938 /* check dimensions or bump user */
939 if (screensy - privheight < 4)
940 {
941 fprintf (stderr, "vchat-client: screen to short (only %d rows, at least %d expected), bailing out.\n", screensy, privheight + 6);
942 snprintf (errstr, ERRSTRSIZE, "vchat-client: screen to short (only %d rows, at least %d expected), bailing out.\n", screensy, privheight + 6);
943 cleanup (0);
944 }
945 if (screensx < 14)
946 {
947 fprintf (stderr, "vchat-client: screen to thin (only %d cols, at least %d expected), bailing out.\n", screensx, 14);
948 snprintf (errstr, ERRSTRSIZE, "vchat-client: screen to thin (only %d cols, at least %d expected), bailing out.\n", screensx, 14);
949 cleanup (0);
950 }
951
952 /* setup our windows */
953 console = newwin (1, screensx, screensy - 2, 0);
954 input = newwin (1, screensx, screensy - 1, 0);
955 if (privheight) private = newwin (privheight, screensx, 0, 0);
956 topic = newwin (1, screensx, privheight, 0);
957 channel = newwin (screensy - (privheight+3), screensx, (privheight+1), 0);
958 output = newwin (1, screensx, 1, 0);
959
960 /* promblems opening windows? bye! */
961 if (!console || !input || !topic || !channel || !output )
962 {
963 fprintf (stderr, "vchat-client: could not open windows, bailing out.\n");
964 cleanup (0);
965 }
966
967 /* Prepare our scrollback buffers */
968 sb_pub = (struct sb_data*)malloc( sizeof(struct sb_data));
969 sb_out = (struct sb_data*)malloc( sizeof(struct sb_data));
970 if( privheight)
971 sb_priv = (struct sb_data*)malloc( sizeof(struct sb_data));
972 else
973 sb_priv = sb_pub;
974
975 memset( sb_pub, 0, sizeof(struct sb_data));
976 memset( sb_priv, 0, sizeof(struct sb_data));
977 memset( sb_out, 0, sizeof(struct sb_data));
978
979 /* set colors for windows */
980 if (has_colors()) {
981 wattrset (console, COLOR_PAIR (9));
982 wattrset (input, COLOR_PAIR (0));
983 wattrset (topic, COLOR_PAIR (9));
984 wbkgd (output, COLOR_PAIR(8));
985 wbkgd (console, COLOR_PAIR (9));
986 wbkgd (channel, COLOR_PAIR (0));
987 wbkgd (input, COLOR_PAIR (0));
988 if (private)
989 wbkgd (private, COLOR_PAIR (0));
990 wbkgd (topic, COLOR_PAIR (9));
991 } else {
992 wattron (console, A_REVERSE);
993 wattron (topic, A_REVERSE);
994 wattron (output, A_REVERSE);
995 wbkgd(output, A_REVERSE);
996 }
997
998 /* set some options */
999 scrollok (channel, TRUE);
1000 if (private)
1001 scrollok (private, TRUE);
1002 scrollok (input, TRUE);
1003 scrollok (output, TRUE);
1004 //idlok(channel,TRUE);
1005 wtimeout (input, 100);
1006
1007 /* setup readlines display */
1008 /* FIXME: present only in newer readline versions
1009 rl_set_screen_size (1, screensx);
1010 */
1011
1012 /* use our callback to draw display */
1013 rl_redisplay_function = vciredraw;
1014
1015 /* get keymap, throw out unwanted binding */
1016 keymap = rl_get_keymap ();
1017 rl_unbind_command_in_map ("clear-screen", keymap);
1018 rl_unbind_command_in_map ("complete", keymap);
1019 rl_unbind_command_in_map ("possible-completions", keymap);
1020 rl_unbind_command_in_map ("insert-completions", keymap);
1021
1022 /* bind CTRL-L to clearmsg() */
1023 rl_bind_key ('J'-'@', (rl_command_func_t *) clearpriv);
1024 rl_bind_key ('O'-'@', (rl_command_func_t *) clearchan);
1025 rl_bind_key ('L'-'@', (rl_command_func_t *) redraw);
1026 rl_bind_key ('B'-'@', (rl_command_func_t *) scrollup);
1027 rl_bind_key ('P'-'@', (rl_command_func_t *) scrollup);
1028 rl_bind_key ('F'-'@', (rl_command_func_t *) scrolldown);
1029 rl_bind_key ('N'-'@', (rl_command_func_t *) scrolldown);
1030 rl_bind_key ('R'-'@', (rl_command_func_t *) scrollwin);
1031 rl_bind_key ('T'-'@', (rl_command_func_t *) shrinkprivwin);
1032 rl_bind_key ('G'-'@', (rl_command_func_t *) growprivwin);
1033 rl_bind_key ('X'-'@', (rl_command_func_t *) toggleprivwin);
1034
1035 /* bind TAB to menu complete from readline */
1036 rl_bind_key ('\t', (rl_command_func_t *) rl_menu_complete);
1037
1038 /* application name for .inputrc - err, we don't load it */
1039 rl_readline_name = "vchat-client";
1040
1041 /* set up nick completion functions .. */
1042#ifdef OLDREADLINE
1043 rl_completion_entry_function = (Function *) ul_nickcomp;
1044 rl_attempted_completion_function = vcccomplete;
1045#else
1046 rl_completion_entry_function = (rl_compentry_func_t *) ul_nickcomp;
1047 rl_attempted_completion_function = (rl_completion_func_t *) vcccomplete;
1048#endif
1049
1050 /* .. and 'line completed' callback */
1051#ifdef OLDREADLINE
1052 rl_callback_handler_install ("", linecomplete);
1053#else
1054 rl_callback_handler_install ("", (rl_vcpfunc_t *) linecomplete);
1055#endif
1056
1057 writeout( ">> Ctrl-X <<");
1058
1059 if (errstr[0] != '\0') {
1060 writeout(errstr);
1061 writeout( " ");
1062 }
1063
1064 writeout (vchat_cl_version);
1065 writeout (vchat_ui_version);
1066 writeout (vchat_io_version);
1067 writeout (vchat_us_version);
1068 writeout (vchat_cm_version);
1069 showout( );
1070}
1071
1072/* render colorized line to window */
1073/* DOES NOT WRAP !!!
1074 CURRENTLY UNUSED
1075 Enable, when needed
1076
1077static void
1078writecolorized( WINDOW *win, unsigned char *string) {
1079 ncurs_attr old_att, new_att;
1080 int i;
1081
1082 WATTR_GET( win, old_att );
1083 new_att = old_att;
1084 for( i = 0; string[ i ]; i++ )
1085 if( string[ i ] == 1 ) {
1086 docolorize( string[++i], &new_att, old_att);
1087 } else {
1088 WATTR_SET( win, new_att );
1089 waddch( win, string[ i ] );
1090 }
1091 WATTR_SET( win, old_att );
1092}
1093*/
1094
1095/* render consoleline to screen */
1096void
1097consoleline (unsigned char *message)
1098{
1099 /* clear console, set string (or default), redraw display */
1100 int i;
1101 ncurs_attr old_att, new_att;
1102
1103 memset( &new_att, 0, sizeof(new_att));
1104 BCOLR_SET( (&new_att), 8 );
1105 wmove (console, 0, 0);
1106 WATTR_GET( console, old_att);
1107 if(sb_pub->scroll!=sb_pub->count) WATTR_SET( console, new_att);
1108
1109 for (i = 0; i < console->_maxx; i++)
1110 waddch (console, ' ');
1111 mvwaddnstr (console, 0, 0, message ? message : consolestr, console->_maxx);
1112 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_SBINF),sb_pub->scroll,sb_pub->count);
1113 mvwaddstr (console, 0, console->_maxx - (strlen(tmpstr)-1),tmpstr);
1114 if (sb_win == 0) mvwaddch (console, 0, console->_maxx,'*');
1115
1116 WATTR_SET( console, old_att);
1117
1118 wnoutrefresh(console);
1119 if(outputshown) {
1120 redrawwin(output);
1121 wnoutrefresh(output);
1122 }
1123 wnoutrefresh(input);
1124 doupdate();
1125}
1126
1127/* render topicline to screen */
1128void
1129topicline (unsigned char *message)
1130{
1131 int i;
1132 ncurs_attr old_att, new_att;
1133
1134 memset( &new_att, 0, sizeof(new_att));
1135 BCOLR_SET( (&new_att), 8 );
1136
1137 /* clear topic, set string (or default), redraw display */
1138 wmove (topic, 0, 0);
1139
1140 WATTR_GET( topic, old_att);
1141 if( private && (sb_priv->scroll!=sb_priv->count))
1142 WATTR_SET( topic, new_att);
1143
1144 for (i = 0; i < topic->_maxx; i++)
1145 waddch (topic, ' ');
1146 mvwaddnstr (topic, 0, 0, message ? message : topicstr, topic->_maxx);
1147 if (private) {
1148 snprintf(tmpstr,TMPSTRSIZE,getformatstr(FS_SBINF),sb_priv->scroll,sb_priv->count);
1149 mvwaddstr (topic, 0, topic->_maxx - (strlen(tmpstr)-1),tmpstr);
1150 if (sb_win == 1) mvwaddch (topic, 0, topic->_maxx,'*');
1151 }
1152 WATTR_SET( topic, old_att);
1153
1154 wnoutrefresh(topic);
1155 if(outputshown) {
1156 redrawwin(output);
1157 wnoutrefresh(output);
1158 }
1159 wnoutrefresh(input);
1160 doupdate();
1161}
1162
1163/* end userinterface */
1164void
1165exitui (void)
1166{
1167 if (ui_init) {
1168 rl_callback_handler_remove ();
1169 endwin ();
1170 ui_init = 0;
1171 }
1172}
1173
1174/* prompt for a nick */
1175/* FIXME: must not be called when used rl_callback_read_char()/userinput()
1176 * before */
1177void
1178nickprompt (void)
1179{
1180 int i;
1181
1182 if (nick)
1183 return;
1184
1185 /* prompt user for nick unless he enters one */
1186 consoleline("Please enter your nickname:");
1187 while (!nick || !nick[0])
1188 {
1189 if (nick)
1190 free (nick);
1191 nick = readline("");
1192 }
1193 setstroption(CF_NICK,nick);
1194
1195 /* try to get readlines stats clean again */
1196 //rl_free_line_state ();
1197 rl_point = 0;
1198 rl_done = 0;
1199 rl_line_buffer[0] = 0;
1200 lastlen = 23;
1201
1202 /* wipe input line and reset cursor */
1203 wmove (input, 0, 0);
1204 for (i = 0; i < input->_maxx; i++)
1205 waddch(input, ' ');
1206 wmove(input, 0, 0);
1207
1208 /* reset consoleline */
1209 consoleline(NULL);
1210}
1211
1212/* special callback for readline, doesn't show the characters */
1213static void
1214vcnredraw (void)
1215{
1216 int i;
1217 unsigned char *passbof="-*-*-*-*-*-*-";
1218
1219 /* wipe input line and reset cursor */
1220 wmove(input, 0, 0);
1221 for (i = 0; i < input->_maxx; i++)
1222 waddch(input, ' ');
1223 wmove(input, 0, 0);
1224
1225 /* draw as many stars as there are characters */
1226 mvwaddnstr(input, 0, 0, &passbof[rl_point%2], 12);
1227 wmove(input, 0, input->_maxx);
1228 wrefresh(input);
1229}
1230
1231/* passphrase callback for OpenSSL */
1232/* FIXME: must not be called when used rl_callback_read_char()/userinput()
1233 * before */
1234int
1235passprompt (char *buf, int size, int rwflag, void *userdata)
1236{
1237 int i;
1238 unsigned char *passphrase = NULL;
1239
1240 /* use special non-revealing redraw function */
1241 /* FIXME: passphrase isn't protected against e.g. swapping */
1242 rl_redisplay_function = vcnredraw;
1243
1244 /* prompt user for non-empty passphrase */
1245 consoleline("Please enter PEM passphrase for private key:");
1246 while (!passphrase || !passphrase[0])
1247 {
1248 if (passphrase)
1249 free (passphrase);
1250 passphrase = readline ("");
1251 }
1252
1253 /* reset redrawing function to default, reset consoleline */
1254 rl_redisplay_function = vciredraw;
1255 consoleline(NULL);
1256
1257 /* copy passphrase to buffer */
1258 strncpy (buf, passphrase, size);
1259
1260 /* try to get readlines stats clean again */
1261 //rl_free_line_state ();
1262 rl_point = 0;
1263 rl_done = 0;
1264 rl_line_buffer[0] = 0;
1265 lastlen = 23;
1266
1267 /* wipe input line and reset cursor */
1268 wmove (input, 0, 0);
1269 for (i = 0; i < input->_maxx; i++)
1270 waddch (input, ' ');
1271 wmove (input, 0, 0);
1272 wrefresh (input);
1273
1274 /* return passphrase to OpenSSL */
1275 return strlen (buf);
1276}
1277
1278/* Filter stuff */
1279static int
1280check_valid_colour( char colour ) {
1281 return !( (colour !='-')&&(colour !='+') && (colour < '0' || colour > '9') &&
1282 (colour < 'A' || colour > 'Z' || !attributes[ colour-'A' ]) &&
1283 (colour < 'a' || colour > 'z' || !attributes[ colour-'a' ]));
1284}
1285
1286
1287/* scans filterlist and removes possible matches
1288 test functions may return:
1289 RMFILTER_RMANDCONT
1290 RMFILTER_RMANDSTOP
1291 RMFILTER_KEEPANDCONT
1292 RMFILTER_KEEPANDSTOP
1293 returns number of removed entries
1294*/
1295static int
1296removefromfilterlist( int(*test)(filt *flt, void *data, char colour), void *data, char colour) {
1297 filt **flt = &filterlist, *tmp;
1298 int removed = 0, stop = 0;
1299
1300 while( *flt && !stop ) {
1301 switch( test( *flt, data, colour ) ) {
1302 case RMFILTER_RMANDSTOP: /* remove */
1303 stop = 1;
1304 case RMFILTER_RMANDCONT:
1305 snprintf( tmpstr, TMPSTRSIZE, " Removed ID: [% 3d] Color: [%c] Regex: [%s] ", (*flt)->id, (*flt)->colour, (*flt)->text);
1306 writeout(tmpstr);
1307 /* Release regex.h resources */
1308 regfree( &((*flt)->regex));
1309 /* free ASCII text memory */
1310 free( (*flt)->text);
1311 /* unlink from list */
1312 tmp = *flt;
1313 *flt = (*flt)->next;
1314 /* free filter block itself */
1315 free( tmp );
1316 /* reflect changes on whole screen */
1317 removed++;
1318 break;
1319 case RMFILTER_KEEPANDSTOP: /* don't remove but stop scanning */
1320 stop = 1;
1321 break;
1322 default:
1323 /* advance in list */
1324 if( *flt ) flt = &((*flt)->next);
1325 break;
1326 }
1327 }
1328 /* return number of removed items */
1329 return removed;
1330}
1331
1332static int
1333test_clear( filt *flt, void *data, char c ) {
1334 if( !c || ( c == flt->colour ) || ( (c == '*') && (flt->colour != '-') && (flt->colour != '+') ) )
1335 return RMFILTER_RMANDCONT;
1336 else
1337 return RMFILTER_KEEPANDCONT;
1338}
1339
1340static int
1341test_simplerm( filt *flt, void *data, char colour) {
1342 if( !strcmp( flt->text, (char*)data))
1343 return test_clear(flt, NULL, colour);
1344 else
1345 return RMFILTER_KEEPANDCONT;
1346}
1347
1348static int
1349test_numericrm( filt *flt, void *data, char colour) {
1350 if( flt->id == (long)data)
1351 return test_clear(flt, NULL, colour);
1352 else
1353 return RMFILTER_KEEPANDCONT;
1354}
1355
1356/* clears filter list */
1357void
1358clearfilters( char colour ) {
1359 flushout( );
1360 if( removefromfilterlist( test_clear, NULL, colour ) ) {
1361 /* There actually WERE items removed */
1362 filtertype = analyzefilters( );
1363 } else {
1364 writeout(" No matches on filter list. ");
1365 }
1366 showout();
1367}
1368
1369/* removes filter pattern */
1370void
1371removefilter( unsigned char *tail ) {
1372 int rmv = 0, val;
1373 unsigned char* end;
1374
1375 flushout( );
1376
1377 rmv = removefromfilterlist( test_simplerm, (void *)tail, 0 );
1378 if(!rmv) {
1379 val = strtol((char*)tail, (char **)&end, 10);
1380 if( (tail != end) && (!*end) )
1381 rmv = removefromfilterlist( test_numericrm, (void *)val, 0);
1382 }
1383
1384 if( rmv ) {
1385 /* There actually WERE items removed */
1386 filtertype = analyzefilters( );
1387 } else {
1388 snprintf( tmpstr, TMPSTRSIZE, " Not on filter list: %s ", tail);
1389 writeout( tmpstr );
1390 }
1391 showout();
1392}
1393
1394static unsigned int uniqueidpool = 1;
1395
1396/* returns unique id for filter pattern or 0 for failure */
1397unsigned int
1398addfilter( char colour, unsigned char *regex ) {
1399 filt *newflt = malloc( sizeof(filt)), **flt = &filterlist;
1400
1401 if( !newflt ) return 0;
1402 flushout( );
1403
1404 /* check colour validity */
1405 if( !check_valid_colour( colour ) ){
1406 free( newflt );
1407 writeout( " Not a valid colour code. " );
1408 showout( );
1409 return 0;
1410 }
1411
1412 if( regcomp( &newflt->regex, regex, REG_ICASE | REG_EXTENDED | REG_NEWLINE) ) {
1413 /* couldn't compile regex ... print error, return */
1414 free( newflt );
1415
1416 snprintf( tmpstr, TMPSTRSIZE, " %s ", regex);
1417 writeout( " Bad regular expression: ");
1418 writeout( tmpstr );
1419 showout( );
1420 return 0;
1421 } else {
1422 int len = strlen(regex) + 1;
1423 /* grab id from ID pool an increase free ID counter */
1424 newflt->id = uniqueidpool++;
1425 newflt->colour = colour;
1426 newflt->next = NULL;
1427 /* take a copy of plain regex text for later identification by user */
1428 newflt->text = malloc( len );
1429 memcpy( newflt->text, regex, len );
1430 }
1431
1432 /* append new filter to filterlist */
1433 while( *flt ) flt=&((*flt)->next);
1434 *flt = newflt;
1435
1436 filtertype = analyzefilters( );
1437
1438 if ( colour == '-' ) {
1439 snprintf( tmpstr, TMPSTRSIZE, " \"%s\" successfully added to ignorance list. ( ID = %d). ", (*flt)->text, (*flt)->id);
1440 } else if( colour == '+' ) {
1441 snprintf( tmpstr, TMPSTRSIZE, " \"%s\" successfully added to zoom list. ( ID = %d). ", (*flt)->text, (*flt)->id);
1442 } else {
1443 snprintf( tmpstr, TMPSTRSIZE, " \"%s\" successfully added to hilitelist. (ID = %d). ", (*flt)->text, (*flt)->id);
1444 }
1445 writeout(tmpstr );
1446 showout( );
1447
1448 return newflt->id;
1449}
1450
1451void
1452listfilters( void ) {
1453 filt *flt = filterlist;
1454 int shownhi = 0, shownign = 0, shownzoom = 0;
1455
1456 flushout( );
1457
1458 while( flt ) {
1459 if( (flt->colour != '-') && (flt->colour != '+')) {
1460 if(!shownhi) {
1461 writeout(" Your hilites:");
1462 shownhi = 1;
1463 }
1464 snprintf( tmpstr, TMPSTRSIZE, " ID: [% 3d] Color: [%c] Regex: [%s]", flt->id, flt->colour, flt->text);
1465 writeout( tmpstr );
1466 }
1467 flt = flt->next;
1468 }
1469
1470 flt = filterlist;
1471
1472 while( flt ) {
1473 if( flt->colour == '-') {
1474 if(!shownign) {
1475 if(shownhi) writeout(" ");
1476 writeout(" You do ignore:");
1477 shownign = 1;
1478 }
1479 snprintf( tmpstr, TMPSTRSIZE, " ID: [% 3d] Regex: [%s]", flt->id, flt->text);
1480 writeout( tmpstr );
1481 }
1482 flt = flt->next;
1483 }
1484
1485 flt = filterlist;
1486
1487 while( flt ) {
1488 if( flt->colour == '+') {
1489 if(!shownzoom) {
1490 if(shownhi || shownign) writeout(" ");
1491 writeout(" On your whitelist:");
1492 shownzoom = 1;
1493 }
1494 snprintf( tmpstr, TMPSTRSIZE, " ID: [% 3d] Regex: [%s]", flt->id, flt->text);
1495 writeout( tmpstr );
1496 }
1497 flt = flt->next;
1498 }
1499
1500 if( !shownign && !shownhi && !shownzoom) {
1501 writeout(" No entries on your filter list. ");
1502 }
1503 showout();
1504}
diff --git a/vchat-user.c b/vchat-user.c
new file mode 100755
index 0000000..4f4990c
--- /dev/null
+++ b/vchat-user.c
@@ -0,0 +1,374 @@
1/*
2 * vchat-client - alpha version
3 * vchat-user.c - functions working with the userlist
4 *
5 * Copyright (C) 2001 Andreas Kotes <count@flatline.de>
6 *
7 * This program is free software. It can be redistributed and/or modified,
8 * provided that this copyright notice is kept intact. This program is
9 * distributed in the hope that it will be useful, but without any warranty;
10 * without even the implied warranty of merchantability or fitness for a
11 * particular purpose. In no event shall the copyright holder be liable for
12 * any direct, indirect, incidental or special damages arising in any way out
13 * of the use of this software.
14 *
15 */
16
17/* general includes */
18#include <stdlib.h>
19#include <stdio.h>
20#include <string.h>
21#include <sys/types.h>
22#include <regex.h>
23#include "vchat.h"
24
25/* version of this module */
26unsigned char *vchat_us_version = "$Id$";
27
28/* externally used variables */
29/* current nick */
30unsigned char *nick = NULL;
31/* current channel */
32int chan = 0;
33/* userlist */
34user *nicks = NULL;
35
36/* add user to userlist */
37void
38ul_add (unsigned char *name, int ignored)
39{
40 user *tmp = NULL;
41
42 /* is it this client? return */
43 if (nick && !strcasecmp (nick, name))
44 return;
45
46 /* no list? create one */
47 if (!nicks)
48 {
49 nicks = malloc (sizeof (user));
50 memset(nicks,0,sizeof(user));
51 nicks->nick = strdup(name);
52 nicks->chan = 0; /* users default in channel 0 */
53 nicks->chan_valid = 0;
54 nicks->next = NULL;
55 }
56 else
57 {
58 /* travel list until end */
59 tmp = nicks;
60 while (tmp)
61 {
62 /* it is this user? return */
63 if (!strcmp (name, tmp->nick))
64 return;
65 /* is there a next user? */
66 if (tmp->next)
67 tmp = tmp->next;
68 else
69 {
70 /* create one */
71 tmp->next = malloc (sizeof (user));
72 tmp = tmp->next;
73 memset(tmp,0,sizeof(user));
74 tmp->nick = strdup(name);
75 tmp->chan = 0;
76 tmp->chan_valid = 0;
77 tmp->next = NULL;
78 tmp = NULL;
79 }
80 }
81 }
82}
83
84/* delete user from userlist */
85void
86ul_del (unsigned char *name, int ignored)
87{
88 user *tmp = NULL, *ltmp = NULL;
89
90 /* is it this client? return */
91 if (nick && !strcmp (nick, name))
92 return;
93 /* no list? return */
94 if (!nicks)
95 return;
96 /* the user on top of list? */
97 if (!strcmp (name, nicks->nick))
98 {
99 /* remove user and copy next in list */
100 tmp = nicks->next;
101 free (nicks);
102 nicks = tmp;
103 return;
104 }
105 /* travel through list, skip first entry */
106 ltmp = nicks;
107 tmp = nicks->next;
108 while (tmp)
109 {
110 /* is it this user? */
111 if (!strcmp (name, tmp->nick))
112 {
113 /* hook next to last, discard this */
114 ltmp->next = tmp->next;
115 free (tmp);
116 return;
117 }
118 /* advance in list */
119 ltmp = tmp;
120 tmp = tmp->next;
121 }
122}
123
124/* let user join a channel */
125void
126ul_join (unsigned char *name, int channel)
127{
128 /* is it this client? handle and return */
129 if (nick && !strcmp (nick, name))
130 {
131 ownjoin (channel);
132 return;
133 } else ul_moveuser(name,channel);
134}
135
136user *
137ul_finduser (unsigned char *name) {
138 user *tmp = nicks;
139 /* search user */
140 while (tmp)
141 {
142 /* is it this user? */
143 if (!strcmp (name, tmp->nick))
144 {
145 return tmp;
146 }
147 /* advance in list */
148 tmp = tmp->next;
149 }
150 return NULL;
151}
152
153unsigned char *
154ul_matchuser( unsigned char *regex) {
155 user *tmp = nicks;
156 unsigned char *dest = tmpstr;
157 regex_t preg;
158
159 *dest = 0;
160 if( !regcomp( &preg, regex, REG_ICASE | REG_EXTENDED | REG_NEWLINE)) {
161 while( tmp ) {
162 /* does the username match? */
163 if( !regexec( &preg, tmp->nick, 0, NULL, 0)) /* append username to list */
164 dest += snprintf ( dest, 256, " %s", tmp->nick);
165 tmp = tmp->next;
166 }
167 }
168 regfree( &preg );
169 return tmpstr;
170}
171
172void
173ul_msgto (unsigned char *name) {
174 user *tmp = ul_finduser(name);
175
176 if (tmp)
177 tmp->messaged |= 1;
178}
179
180void
181ul_msgfrom (unsigned char *name) {
182 user *tmp = ul_finduser(name);
183
184 if (tmp)
185 tmp->messaged |= 2;
186}
187
188/* set channel of user */
189void
190ul_moveuser (unsigned char *name, int channel) {
191 user *tmp = ul_finduser(name);
192
193 if (tmp) {
194 /* store channel information and mark it valid */
195 tmp->chan = channel;
196 tmp->chan_valid = 1;
197 }
198}
199
200/* let user leave a channel */
201void
202ul_leave (unsigned char *name, int channel)
203{
204 user *tmp = ul_finduser(name);
205 /* is it this client? handle and return */
206 if (nick && !strcmp (nick, name))
207 {
208 ownleave (channel);
209 return;
210 }
211
212 if (tmp)
213 {
214 /* mark channel information invalid */
215 tmp->chan_valid = 0;
216 return;
217 }
218}
219
220/* let user change nick */
221void
222ul_nickchange (unsigned char *oldnick, unsigned char *newnick)
223{
224 user *tmp = ul_finduser(oldnick);
225 /* is it this client? handle and return */
226 if (nick && !strcmp (nick, oldnick))
227 {
228 ownnickchange (newnick);
229 return;
230 }
231 if (tmp)
232 {
233 /* exchange nickname */
234 free (tmp->nick);
235 tmp->nick = strdup (newnick);
236 return;
237 }
238}
239
240/* clear userlist */
241void
242ul_clear (void)
243{
244 user *tmp = nicks, *tmp2;
245 /* walk list */
246 while (tmp)
247 {
248 /* store next, delete this */
249 tmp2 = tmp->next;
250 free (tmp->nick);
251 free (tmp);
252 /* advance */
253 tmp = tmp2;
254 }
255 /* mark list empty */
256 nicks = NULL;
257}
258
259int ulnc_casenick(user *tmp, const unsigned char *text, int len, int value) {
260 return (!strncmp(tmp->nick, text, len));
261}
262
263int ulnc_ncasenick(user *tmp, const unsigned char *text, int len, int value) {
264 return (!strncasecmp(tmp->nick, text, len));
265}
266
267unsigned char *
268ulnc_complete (const unsigned char *text, int state, int value, int (*checkfn)(user *,const unsigned char *,int,int)) {
269 static int len;
270 static user *tmp;
271 unsigned char *name;
272
273 /* first round? reset pointers! */
274 if (!state)
275 {
276 tmp = nicks;
277 len = strlen (text);
278 }
279
280 /* walk list .. */
281 while (tmp)
282 {
283 /* we found a match? */
284 if (checkfn(tmp,text,len,value))
285 {
286 /* copy nick, advance pointer for next call, return nick */
287 name = tmp->nick;
288 tmp = tmp->next;
289 return name;
290 }
291 else
292 {
293 tmp = tmp->next;
294 }
295 }
296 return NULL;
297}
298
299/* nick completion functions for readline in vchat-ui.c */
300unsigned char *
301ul_nickcomp (const unsigned char *text, int state)
302{
303 int ncasemode;
304 unsigned char *name = NULL;
305 if (!state) ncasemode = 0;
306 if (!ncasemode) {
307 name = ulnc_complete(text,state,0,ulnc_casenick);
308 if (!state && !name) ncasemode = 1;
309 }
310 if (ncasemode)
311 name = ulnc_complete(text,state,0,ulnc_ncasenick);
312 if (name)
313 return strdup(name);
314 else
315 return NULL;
316}
317
318int ulnc_casenickc(user *tmp, const unsigned char *text, int len, int value) {
319 return (!strncmp(tmp->nick, text, len) && (tmp->chan_valid) && (tmp->chan == value));
320}
321
322int ulnc_ncasenickc(user *tmp, const unsigned char *text, int len, int value) {
323 return (!strncasecmp(tmp->nick, text, len) && (tmp->chan_valid) && (tmp->chan == value));
324}
325
326/* nick completion for channel, used by vchat-ui.c */
327unsigned char *
328ul_cnickcomp (const unsigned char *text, int state)
329{
330 int ncasemode;
331 static unsigned char *name = NULL;
332
333 if (!state) ncasemode = 0;
334 if (!ncasemode) {
335 name = ulnc_complete(text,state,chan,ulnc_casenickc);
336 if (!state && !name) ncasemode = 1;
337 }
338 if (ncasemode)
339 name = ulnc_complete(text,state,chan,ulnc_ncasenickc);
340 if (name) {
341 snprintf(tmpstr,TMPSTRSIZE,"%s:",name);
342 return strdup(tmpstr);
343 } else
344 return NULL;
345}
346
347int ulnc_casenickm(user *tmp, const unsigned char *text, int len, int value) {
348 return (!strncmp(tmp->nick, text, len) && (tmp->messaged));
349}
350
351int ulnc_ncasenickm(user *tmp, const unsigned char *text, int len, int value) {
352 return (!strncasecmp(tmp->nick, text, len) && (tmp->messaged));
353}
354
355/* nick completion for channel, used by vchat-ui.c */
356unsigned char *
357ul_mnickcomp (const unsigned char *text, int state)
358{
359 int ncasemode;
360 static unsigned char *name = NULL;
361
362 if (!state) ncasemode = 0;
363 if (!ncasemode) {
364 name = ulnc_complete(text,state,chan,ulnc_casenickm);
365 if (!state && !name) ncasemode = 1;
366 }
367 if (ncasemode)
368 name = ulnc_complete(text,state,chan,ulnc_ncasenickm);
369 if (name) {
370 snprintf(tmpstr,TMPSTRSIZE,".m %s",name);
371 return strdup(tmpstr);
372 } else
373 return NULL;
374}
diff --git a/vchat.h b/vchat.h
new file mode 100755
index 0000000..5cfc33f
--- /dev/null
+++ b/vchat.h
@@ -0,0 +1,210 @@
1/*
2 * vchat-client - alpha version
3 * vchat.h - includefile with glue structures an functions
4 *
5 * Copyright (C) 2001 Andreas Kotes <count@flatline.de>
6 *
7 * This program is free software. It can be redistributed and/or modified,
8 * provided that this copyright notice is kept intact. This program is
9 * distributed in the hope that it will be useful, but without any warranty;
10 * without even the implied warranty of merchantability or fitness for a
11 * particular purpose. In no event shall the copyright holder be liable for
12 * any direct, indirect, incidental or special damages arising in any way out
13 * of the use of this software.
14 *
15 */
16
17/* user structure */
18struct user
19{
20 unsigned char *nick; /* nick of user */
21 int chan; /* channel user is on */
22 int chan_valid; /* are we sure he is? */
23 int client_pv; /* client protocol version */
24 int messaged; /* did we message with this user? */
25 struct user *next; /* next user in linked list */
26};
27typedef struct user user;
28/* userlist from vchat-user.c */
29extern user *nicks;
30
31/* servermessage types */
32typedef enum { SM_IGNORE, SM_INFO, SM_USERINFO, SM_CHANNEL, SM_ERROR } smtype;
33
34/* servermessage structure */
35struct servermessage
36{
37 unsigned char id[4]; /* three-character message id */
38 smtype type; /* message type */
39 void (*funct) (unsigned char *); /* function used by client */
40 void (*hook) (unsigned char *); /* function hook for scripting */
41};
42typedef struct servermessage servermessage;
43
44/* configuration types and variable numbers */
45typedef enum { CO_NIL, CO_STR, CO_INT } conftype;
46typedef enum { CF_NIL, CF_NICK, CF_FROM, CF_SERVERHOST, CF_SERVERPORT,
47CF_CIPHERSUITE, CF_CONFIGFILE, CF_CERTFILE, CF_KEYFILE, CF_FORMFILE,
48CF_USESSL, CF_USECERT, CF_PRIVHEIGHT, CF_HSCROLL, CF_CHANNEL, CF_USETIME,
49CF_SCROLLBPRIV, CF_SCROLLBACK, CF_SCROLLBPRIVT, CF_SCROLLBACKT } confopt;
50/* format strings */
51typedef enum { FS_PLAIN, FS_CHAN, FS_PRIV, FS_SERV, FS_GLOB, FS_DBG, FS_ERR,
52FS_IDLE, FS_TIME, FS_TOPICW, FS_NOTOPICW, FS_CONSOLE, FS_CONNECTED, FS_TOPIC,
53FS_NOTOPIC, FS_CHGTOPIC, FS_USONLINE, FS_USMATCH, FS_SIGNON, FS_SIGNOFF, FS_JOIN,
54FS_LEAVE, FS_NICKCHANGE, FS_UNKNOWNMSG, FS_BOGUSMSG, FS_RXPUBURL, FS_MYPUBURL,
55FS_RXPUBMSG, FS_MYPUBMSG, FS_TXPUBMSG, FS_RXPRIVMSG, FS_TXPRIVMSG,
56FS_BGPRIVMSG, FS_PUBACTION, FS_TXPUBACTION, FS_BGTXPUBACTION, FS_COMMAND,
57FS_LOCALCOMMAND, FS_BOGUSCOMMAND, FS_SBINF, FS_MISSTYPED, FS_UNKNCMD, FS_BADREGEX,
58FS_ERR_STRING } formtstr;
59
60/* configoption structure */
61struct configoption
62{
63 confopt id;
64 conftype type;
65 unsigned char *varname;
66 unsigned char *defaultvalue;
67 unsigned char *value;
68 unsigned char **localvar;
69};
70
71typedef struct configoption configoption;
72
73/* format strings */
74struct formatstring
75{
76 formtstr id;
77 unsigned char *idstring;
78 unsigned char *formatstr;
79};
80typedef struct formatstring formatstring;
81
82/* static tmpstr in all modules */
83#define TMPSTRSIZE 1024
84static unsigned char tmpstr[TMPSTRSIZE];
85
86extern unsigned char *nick;
87extern int chan;
88
89extern unsigned int loggedin;
90
91/* vchat-client.c */
92#define ERRSTRSIZE 1024
93extern unsigned char errstr[];
94extern unsigned char *vchat_cl_version;
95void cleanup(int signal);
96
97/* configuration helper funktions from vchat-client.c */
98unsigned char *getformatstr (formtstr id);
99unsigned char *getstroption (confopt option);
100void setstroption (confopt option, unsigned char *string);
101int getintoption (confopt option);
102void setintoption (confopt option, int value);
103
104/* vchat-user.c */
105extern unsigned char *vchat_us_version;
106
107/* add / delete user */
108void ul_add (unsigned char *nick, int ignored);
109void ul_del (unsigned char *nick, int ignored);
110
111/* clear userlist */
112void ul_clear ();
113
114/* channel join / leave */
115void ul_join (unsigned char *nick, int channel);
116void ul_leave (unsigned char *nick, int channel);
117
118/* nickchange */
119void ul_nickchange (unsigned char *oldnick, unsigned char *newnick);
120
121/* place user in channel */
122void ul_moveuser (unsigned char *nick, int channel);
123
124/* message nick completion */
125void ul_msgto (unsigned char *nick);
126void ul_msgfrom (unsigned char *nick);
127
128/* nick-completion for vchat-ui.c */
129unsigned char *ul_nickcomp (const unsigned char *text, int state);
130unsigned char *ul_cnickcomp (const unsigned char *text, int state);
131unsigned char *ul_mnickcomp (const unsigned char *text, int state);
132
133/* try to find user by substring */
134unsigned char *ul_matchuser ( unsigned char *substr);
135
136/* vchat-ui.c */
137extern unsigned char *vchat_ui_version;
138
139/* topic and console strings */
140#define TOPICSTRSIZE 1024
141#define CONSOLESTRSIZE 1024
142extern unsigned char topicstr[];
143extern unsigned char consolestr[];
144
145/* init / exit functions */
146void initui (void);
147void exitui (void);
148
149/* called from eventloop in vchat-client.c */
150void userinput (void);
151
152/* display various messages */
153int writechan (unsigned char *str);
154int writepriv (unsigned char *str);
155void writeout (unsigned char *str);
156void showout (void);
157void flushout (void);
158void hideout (void);
159int writecf (formtstr id, unsigned char *str);
160
161extern int outputcountdown;
162
163/* update console / topic window */
164void consoleline (unsigned char *);
165void topicline (unsigned char *);
166
167/* prompt for nick or password */
168void nickprompt (void);
169int passprompt (char *buf, int size, int rwflag, void *userdata);
170
171/* filter functions */
172void refilterscrollback( void);
173
174unsigned int addfilter ( char colour, unsigned char *regex );
175void removefilter ( unsigned char *line );
176void listfilters ( void );
177void clearfilters ( char colour );
178
179/* vchat-protocol.c */
180extern unsigned char *vchat_io_version;
181
182/* connect/disconnect */
183int vcconnect (unsigned char *server, unsigned int port);
184void vcdisconnect ();
185
186/* network I/O */
187void networkinput (void);
188void networkoutput (unsigned char *);
189
190/* helpers for vchat-user.c */
191void ownjoin (int channel);
192void ownleave (int channel);
193void ownnickchange (unsigned char *newnick);
194
195/* vchat-commands.c */
196extern unsigned char *vchat_cm_version;
197void command_version ( unsigned char *tail);
198
199/* user input */
200void handleline (unsigned char *);
201
202/* struct for defining "/command" handlers */
203typedef struct {
204 int number;
205 unsigned char name[8];
206 int len;
207 void (*handler)(unsigned char *);
208 unsigned char *short_help;
209 unsigned char *help;
210} commandentry;
diff --git a/vchatrc.ex b/vchatrc.ex
new file mode 100755
index 0000000..cb399a3
--- /dev/null
+++ b/vchatrc.ex
@@ -0,0 +1,61 @@
1# This is the vchatrc example thanks to Pylon for this great work
2#
3# Auto-set Nickname during login
4#nick = noname
5
6# Auto-set from-tag during login
7#from = vc-alpha-0.15
8
9# Server-host to connect to
10#host = pulse.flatline.de
11
12# Port on Server
13#port = 2325
14
15# Type of ciphers at the SSL-handshake;
16# further information in the
17# OpenSSL/mod_ssl/Apache-documentation
18#ciphers = HIGH:MEDIUM
19
20# Location of the config-file
21#conffile = ~/.vchat/config
22
23# Location of the certificate
24#certfile = ~/.vchat/cert
25
26# Location of the key
27#keyfile = ~/.vchat/key
28
29# File with special Formats of messages,
30# colours etc
31#formatfile = ~/.vchat/formats
32
33# Connect via ssl [0|1]
34#usessl = 1
35
36# Use the certificate for connecting [0|1]
37#usecert = 1
38
39# Show the time of the message at start of line [0|1]
40#usetime = 1
41
42# Message window sizes n lines
43#messages = 0
44
45# Input-line scrolling every n-th character
46#hscroll = 5
47
48# Auto-join channel n during login
49#channel = 0
50
51# Length of the private-window buffer (0 disables)
52#privscrollb = 2048
53
54# Length of the public-window buffer (0 disables)
55#scrollback = 8192
56
57# TTL in seconds of private-messages (0 disables)
58#privscrollt = 0
59
60# TTL in seconds of public-messages (0 disables)
61#scrolltime = 86400